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

使用Volley与CodeIgniter上传Bitmap至服务器报错:未选择文件

嗨,这个问题我之前帮朋友排查过!核心原因是Volley自带的StringRequest默认不支持multipart/form-data格式的文件上传,而CodeIgniter的文件上传组件只认这种格式的请求,所以才会弹出“you did not select a file to upload”的报错。下面是具体的解决步骤:

解决Volley上传Bitmap到CodeIgniter的文件报错问题

第一步:自定义支持Multipart的Request类

Volley原生没有提供文件上传的请求类,我们需要自己写一个MultipartRequest来构造符合要求的请求体:

import com.android.volley.AuthFailureError;
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.HttpHeaderParser;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Map;

public class MultipartRequest extends Request<NetworkResponse> {
    private final Response.Listener<NetworkResponse> mListener;
    private final Response.ErrorListener mErrorListener;
    private final Map<String, String> mHeaders;
    private final Map<String, String> mParams;
    private final Map<String, DataPart> mFileParams;

    public MultipartRequest(int method, String url,
                            Response.Listener<NetworkResponse> listener,
                            Response.ErrorListener errorListener,
                            Map<String, String> headers,
                            Map<String, String> params,
                            Map<String, DataPart> fileParams) {
        super(method, url, errorListener);
        this.mListener = listener;
        this.mErrorListener = errorListener;
        this.mHeaders = headers;
        this.mParams = params;
        this.mFileParams = fileParams;
    }

    @Override
    public Map<String, String> getHeaders() throws AuthFailureError {
        return mHeaders != null ? mHeaders : super.getHeaders();
    }

    @Override
    public String getBodyContentType() {
        return "multipart/form-data; boundary=boundary";
    }

    @Override
    public byte[] getBody() throws AuthFailureError {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        String boundary = "boundary";

        // 添加普通文本参数(如果有的话)
        if (mParams != null && !mParams.isEmpty()) {
            for (Map.Entry<String, String> entry : mParams.entrySet()) {
                try {
                    bos.write(("--" + boundary + "\r\n").getBytes());
                    bos.write(("Content-Disposition: form-data; name=\"" + entry.getKey() + "\"\r\n\r\n").getBytes());
                    bos.write((entry.getValue() + "\r\n").getBytes());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        // 添加Bitmap转成的文件参数
        if (mFileParams != null && !mFileParams.isEmpty()) {
            for (Map.Entry<String, DataPart> entry : mFileParams.entrySet()) {
                DataPart dataPart = entry.getValue();
                try {
                    bos.write(("--" + boundary + "\r\n").getBytes());
                    bos.write(("Content-Disposition: form-data; name=\"" + entry.getKey() + "\"; filename=\"" + dataPart.getFileName() + "\"\r\n").getBytes());
                    bos.write(("Content-Type: " + dataPart.getMimeType() + "\r\n\r\n").getBytes());
                    bos.write(dataPart.getContent());
                    bos.write("\r\n".getBytes());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        // 结束请求体边界
        try {
            bos.write(("--" + boundary + "--\r\n").getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }

        return bos.toByteArray();
    }

    @Override
    protected Response<NetworkResponse> parseNetworkResponse(NetworkResponse response) {
        return Response.success(response, HttpHeaderParser.parseCacheHeaders(response));
    }

    @Override
    protected void deliverResponse(NetworkResponse response) {
        mListener.onResponse(response);
    }

    @Override
    public void deliverError(VolleyError error) {
        mErrorListener.onErrorResponse(error);
    }

    // 封装文件数据的内部类
    public static class DataPart {
        private String fileName;
        private byte[] content;
        private String mimeType;

        public DataPart(String fileName, byte[] content, String mimeType) {
            this.fileName = fileName;
            this.content = content;
            this.mimeType = mimeType;
        }

        public String getFileName() { return fileName; }
        public byte[] getContent() { return content; }
        public String getMimeType() { return mimeType; }
    }
}

第二步:修改你的uploadImage方法

把Bitmap转换成字节数组,用自定义的MultipartRequest发送请求:

public void uploadImage(final Context context, final Bitmap bitmap) {
    String url = "https://zenosama1111.000webhostapp.com/Upload/do_upload";
    RequestQueue requestQueue = Volley.newRequestQueue(context);

    // 将Bitmap压缩成字节数组
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    bitmap.compress(Bitmap.CompressFormat.JPEG, 90, baos);
    byte[] imageBytes = baos.toByteArray();

    // 构造文件参数,注意key要和后端接收的字段名一致(比如这里的"userfile")
    Map<String, MultipartRequest.DataPart> fileParams = new HashMap<>();
    fileParams.put("userfile", new MultipartRequest.DataPart("uploaded_image.jpg", imageBytes, "image/jpeg"));

    // 如果需要传递普通参数,在这里添加
    Map<String, String> params = new HashMap<>();
    // params.put("user_id", "123");

    MultipartRequest request = new MultipartRequest(Request.Method.POST, url,
            response -> {
                // 上传成功的处理逻辑
                String responseStr = new String(response.data);
                Log.d("UploadResult", "Success: " + responseStr);
            },
            error -> {
                // 上传失败的处理逻辑
                Log.e("UploadError", "Failed: " + error.getMessage());
            },
            null, // 请求头可以传null,有需要再添加
            params,
            fileParams);

    requestQueue.add(request);
}

第三步:检查CodeIgniter后端配置

确保后端接收的字段名和Volley里的key一致,同时检查上传目录权限:

public function do_upload() {
    $config['upload_path'] = './uploads/'; // 确保这个目录存在且有写入权限
    $config['allowed_types'] = 'gif|jpg|png|jpeg';
    $config['max_size'] = 2048; // 限制最大2MB,可按需调整

    $this->load->library('upload', $config);

    // 这里的"userfile"要和Volley里的fileParams的key完全一致
    if (!$this->upload->do_upload('userfile')) {
        $error = array('error' => $this->upload->display_errors());
        echo json_encode($error);
    } else {
        $data = array('upload_data' => $this->upload->data());
        echo json_encode($data);
    }
}

额外注意点:

  • 服务器的uploads目录必须设置写入权限(一般设为755或777,根据服务器环境调整)
  • 如果后端开启了CSRF验证,记得在请求头里添加CSRF Token参数

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 06:49:43