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

Android通过Volley与PHP交互时多图上传出现Unexpected response code 302错误的求助

解决Volley多图上传时的302重定向错误

看起来你遇到的302错误核心原因是多图转Base64后的请求体体积过大,超过了Volley默认的请求大小限制或者PHP服务器的配置上限,再加上你传递数据的格式不够规范,双重因素导致了服务器返回重定向响应。下面一步步帮你解决:

一、先修正数据传递的格式(用标准JSON替代ArrayList.toString())

你之前用image.toString()把ArrayList转成字符串,生成的是类似[base64str1, base64str2]的非标准格式,不仅体积冗余,PHP端解析也容易出问题。换成标准JSON格式传递:

方式1:用Gson快速转JSON(需要引入Gson依赖)

// 把Base64字符串列表转成标准JSON字符串
String jsonImages = new Gson().toJson(encodedImageArrayList);
map.put("images", jsonImages);

方式2:用Android自带JSONArray(无需额外依赖)

JSONArray jsonArray = new JSONArray();
for(String encodedImg : encodedImageArrayList) {
    jsonArray.put(encodedImg);
}
map.put("images", jsonArray.toString());

PHP端对应的解析代码也要同步调整:

$imagesJson = $_POST['images'];
// 把JSON字符串转成PHP数组
$imagesArray = json_decode($imagesJson, true);

// 遍历处理每张图片
foreach($imagesArray as $encoded) {
    $imageBytes = base64_decode($encoded);
    // 这里写保存图片或其他业务逻辑
}

二、增大Volley的请求体大小限制

Volley默认的请求体上限很小(约2MB),多图Base64编码后很容易超标。你需要自定义HttpStack来提升这个限制:

// 创建自定义HttpStack,设置最大允许的请求体大小(比如10MB)
HttpStack customStack = new HurlStack() {
    @Override
    protected HttpURLConnection createConnection(URL url) throws IOException {
        HttpURLConnection conn = super.createConnection(url);
        // 设置固定流式传输大小,单位是字节,10*1024*1024=10MB
        conn.setFixedLengthStreamingMode(10 * 1024 * 1024);
        return conn;
    }
};

// 用自定义栈创建RequestQueue
RequestQueue requestQueue = Volley.newRequestQueue(getApplicationContext(), customStack);

三、调整PHP服务器的配置

PHP默认的POST请求大小和文件上传限制也可能不够,需要修改配置:

方法1:修改php.ini文件

找到php.ini里的以下参数,调整为合适的值(比如10MB):

post_max_size = 10M
upload_max_filesize = 10M

方法2:在PHP脚本开头临时设置

如果无法修改php.ini,可以在接口脚本顶部添加:

ini_set('post_max_size', '10M');
ini_set('upload_max_filesize', '10M');

四、进阶优化:放弃Base64,用Multipart直接传图片

Base64会让图片体积膨胀约30%,是导致请求过大的根本原因之一。更高效的方式是用MultipartRequest直接上传原始图片文件,Volley没有内置这个请求类,你可以自定义一个:

public class MultipartRequest extends Request<String> {
    private final Map<String, String> params;
    private final Map<String, File> fileParams;
    private final Response.Listener<String> listener;
    private static final String BOUNDARY = "volley-multipart-" + System.currentTimeMillis();
    private static final String LINE_END = "\r\n";
    private static final String TWO_HYPHENS = "--";

    public MultipartRequest(int method, String url, Map<String, String> params, Map<String, File> fileParams,
                            Response.Listener<String> listener, Response.ErrorListener errorListener) {
        super(method, url, errorListener);
        this.params = params;
        this.fileParams = fileParams;
        this.listener = listener;
    }

    @Override
    protected Map<String, String> getParams() throws AuthFailureError {
        return params;
    }

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

    @Override
    public byte[] getBody() throws AuthFailureError {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(bos);
        try {
            // 写入普通参数
            for (Map.Entry<String, String> entry : params.entrySet()) {
                buildTextPart(dos, entry.getKey(), entry.getValue());
            }
            // 写入图片文件
            for (Map.Entry<String, File> entry : fileParams.entrySet()) {
                buildFilePart(dos, entry.getKey(), entry.getValue());
            }
            // 写入结束边界
            dos.writeBytes(TWO_HYPHENS + BOUNDARY + TWO_HYPHENS + LINE_END);
            return bos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    private void buildTextPart(DataOutputStream dos, String paramName, String paramValue) throws IOException {
        dos.writeBytes(TWO_HYPHENS + BOUNDARY + LINE_END);
        dos.writeBytes("Content-Disposition: form-data; name=\"" + paramName + "\"" + LINE_END);
        dos.writeBytes(LINE_END);
        dos.writeBytes(paramValue + LINE_END);
    }

    private void buildFilePart(DataOutputStream dos, String paramName, File file) throws IOException {
        dos.writeBytes(TWO_HYPHENS + BOUNDARY + LINE_END);
        dos.writeBytes("Content-Disposition: form-data; name=\"" + paramName + "\"; filename=\"" + file.getName() + "\"" + LINE_END);
        dos.writeBytes("Content-Type: image/jpeg" + LINE_END); // 根据图片类型调整
        dos.writeBytes(LINE_END);

        FileInputStream fis = new FileInputStream(file);
        byte[] buffer = new byte[1024];
        int len;
        while ((len = fis.read(buffer)) != -1) {
            dos.write(buffer, 0, len);
        }
        fis.close();
        dos.writeBytes(LINE_END);
    }

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

    @Override
    protected void deliverResponse(String response) {
        listener.onResponse(response);
    }
}

使用方式

把图片URI转成File对象后,直接传入MultipartRequest:

Map<String, File> imageFiles = new HashMap<>();
// 假设你已经将图片URI转换为File对象file1、file2
imageFiles.put("image1", file1);
imageFiles.put("image2", file2);

MultipartRequest request = new MultipartRequest(Request.Method.POST, "你的PHP接口地址",
        new HashMap<>(), imageFiles,
        response -> {
            // 处理成功响应
        },
        error -> {
            // 处理错误
        });

requestQueue.add(request);

PHP端接收Multipart文件的代码:

// 遍历上传的文件
foreach($_FILES as $file) {
    $tempPath = $file['tmp_name'];
    $targetPath = "uploads/" . $file['name'];
    move_uploaded_file($tempPath, $targetPath);
}

这种方式不仅避免了Base64的体积膨胀,还更符合HTTP文件上传的标准规范,能从根源上解决请求过大的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 06:54:03