Android大图片分按钮选择上传至PHP服务器遇Bitmap为空问题
解决Bitmap为空及大图片上传至PHP服务器的问题
看起来你遇到的核心问题是**bitmap变量未初始化导致为空**,同时你的Volley文件上传逻辑也存在错误——直接传文件路径给服务器是没用的,服务器需要的是图片的二进制数据。另外针对3MB以上的大图片,直接转Bitmap还容易引发OOM(内存溢出),我们需要调整上传逻辑。
问题根源分析
- 你的代码中
uploadBitmap方法里调用了getFileDataFromDrawable(bitmap),但bitmap变量从未被定义和赋值,自然为空。 - 使用
VolleyMultipartRequest上传文件时,应该通过重写getByteData()方法来传递文件二进制数据,而不是在getParams()里传文件路径。 - 原代码中
getPath方法缺少必要的导入语句,会导致编译错误。
修改后的完整代码
import android.Manifest; import android.content.Intent; import android.content.pm.PackageManager; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.provider.MediaStore; import android.provider.Settings; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.util.Base64; import android.view.View; import android.widget.ImageView; import android.widget.Toast; import com.android.volley.NetworkResponse; import com.android.volley.Request; import com.android.volley.Response; import com.android.volley.VolleyError; import com.android.volley.toolbox.Volley; import org.json.JSONException; import org.json.JSONObject; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.HashMap; import java.util.Map; import android.support.v4.content.CursorLoader; // 补上缺失的导入 public class MainActivity extends AppCompatActivity { // ImageView to display image selected ImageView imageView; String filePath; Bitmap bitmap; // 新增Bitmap变量 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // initializing views imageView = findViewById(R.id.imageView); // checking the permission if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:" + getPackageName())); finish(); startActivity(intent); return; } // 选择图片按钮点击事件 findViewById(R.id.buttonChooseImage).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent i = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); startActivityForResult(i, 100); } }); // 上传图片按钮点击事件 findViewById(R.id.buttonUploadImage).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (filePath == null) { Toast.makeText(MainActivity.this, "请先选择图片", Toast.LENGTH_SHORT).show(); return; } // 调用上传方法 uploadBitmap(); } }); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == 100 && resultCode == RESULT_OK && data != null) { // 获取图片Uri Uri imageUri = data.getData(); filePath = getPath(imageUri); // 加载图片到ImageView(大图片建议压缩,避免OOM) BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 2; // 压缩为原图的1/2,可根据需要调整 bitmap = BitmapFactory.decodeFile(filePath, options); imageView.setImageBitmap(bitmap); } } // 获取图片路径 private String getPath(Uri contentUri) { String[] proj = { MediaStore.Images.Media.DATA }; CursorLoader loader = new CursorLoader(getApplicationContext(), contentUri, proj, null, null, null); Cursor cursor = loader.loadInBackground(); int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); cursor.moveToFirst(); String result = cursor.getString(column_index); cursor.close(); return result; } // 图片转Base64(如果服务器需要Base64格式) private String getFileDataFromDrawable(Bitmap bitmap) { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 80, byteArrayOutputStream); // 压缩质量80% return Base64.encodeToString(byteArrayOutputStream.toByteArray(), Base64.DEFAULT); } // 上传图片方法 private void uploadBitmap() { VolleyMultipartRequest volleyMultipartRequest = new VolleyMultipartRequest(Request.Method.POST, EndPoints.UPLOAD_URL, new Response.Listener<NetworkResponse>() { @Override public void onResponse(NetworkResponse response) { try { JSONObject obj = new JSONObject(new String(response.data)); Toast.makeText(getApplicationContext(), obj.getString("message"), Toast.LENGTH_SHORT).show(); } catch (JSONException e) { e.printStackTrace(); } } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { Toast.makeText(getApplicationContext(), error.getMessage(), Toast.LENGTH_SHORT).show(); } }) { // 如果服务器需要接收Base64格式的图片,重写getParams @Override protected Map<String, String> getParams() { Map<String, String> params = new HashMap<>(); params.put("pic", getFileDataFromDrawable(bitmap)); // 传递Base64字符串 return params; } // 如果服务器需要接收二进制文件,重写getByteData(推荐大图片用这个方式) /* @Override protected Map<String, DataPart> getByteData() { Map<String, DataPart> params = new HashMap<>(); long timestamp = System.currentTimeMillis(); params.put("pic", new DataPart(timestamp + ".jpg", getFileBytes(filePath))); return params; } */ }; // 添加请求到Volley队列 Volley.newRequestQueue(this).add(volleyMultipartRequest); } // 获取文件字节数组(用于二进制上传) private byte[] getFileBytes(String filePath) { File file = new File(filePath); ByteArrayOutputStream bos = new ByteArrayOutputStream(); try (FileInputStream fis = new FileInputStream(file)) { byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = fis.read(buffer)) != -1) { bos.write(buffer, 0, bytesRead); } } catch (IOException e) { e.printStackTrace(); } return bos.toByteArray(); } }
关键修改说明
- 初始化Bitmap变量:在类中新增
Bitmap bitmap;,并在onActivityResult中通过BitmapFactory.decodeFile加载图片(同时加入压缩避免大图片OOM)。 - 修正上传逻辑:
- 如果服务器接收Base64格式:在
getParams中传递转换后的Base64字符串。 - 如果服务器接收二进制文件(更适合大图片):注释掉
getParams,启用getByteData方法,直接传递文件字节流。
- 如果服务器接收Base64格式:在
- 添加缺失的导入:补上
CursorLoader和Cursor的导入语句,解决编译错误。 - 增加上传前检查:点击上传按钮时先判断是否已选择图片,避免空指针。
- 大图片优化:使用
BitmapFactory.Options.inSampleSize压缩图片,或者直接用文件字节流上传,减少内存占用。
额外提示
- 确保你的
VolleyMultipartRequest类是正确实现的(如果没有自定义这个类,需要先实现它,或者使用第三方库中的Multipart请求)。 - 针对Android 10及以上版本,直接获取文件路径的方式可能受限,建议使用
MediaStore的API来读取图片数据,或者使用文件选择器库。 - PHP服务器端需要对应处理上传:如果是Base64,就解码后保存;如果是二进制文件,就通过
$_FILES获取并保存。
内容的提问来源于stack exchange,提问作者Vlasmar
相关产品推荐
相关产品推荐

