You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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方法,直接传递文件字节流。
  • 添加缺失的导入:补上CursorLoader和Cursor的导入语句,解决编译错误。
  • 增加上传前检查:点击上传按钮时先判断是否已选择图片,避免空指针。
  • 大图片优化:使用BitmapFactory.Options.inSampleSize压缩图片,或者直接用文件字节流上传,减少内存占用。

额外提示

  • 确保你的VolleyMultipartRequest类是正确实现的(如果没有自定义这个类,需要先实现它,或者使用第三方库中的Multipart请求)。
  • 针对Android 10及以上版本,直接获取文件路径的方式可能受限,建议使用MediaStore的API来读取图片数据,或者使用文件选择器库。
  • PHP服务器端需要对应处理上传:如果是Base64,就解码后保存;如果是二进制文件,就通过$_FILES获取并保存。

内容的提问来源于stack exchange,提问作者Vlasmar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:04:47