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
相关产品推荐
相关产品推荐

