同一连接中URL编码数据并上传文件失败:问题排查与实现方案
问题分析与解决方案
你遇到的问题核心在于没有遵循multipart/form-data请求的格式规范——你直接把URL编码的表单数据写入了输出流,但multipart请求要求每个参数(无论是普通文本还是文件)都必须作为一个独立的"part",用你定义的boundary分隔开,并且带有正确的头信息。直接写入URL编码的数据会破坏整个请求的结构,导致服务器无法正确解析。
要在同一个POST请求中同时发送普通数据和文件,所有内容都需要封装在multipart的各个part里。普通文本数据也要作为一个单独的part,和文件part一样用boundary分隔,并且指定name属性。
修正后的Android代码
HttpURLConnection conn = null; DataOutputStream dos = null; String existingFileName = Environment.getExternalStorageDirectory().getAbsolutePath() + "/recorded_audio.3gp"; String lineEnd = "\r\n"; String twoHyphens = "--"; String boundary = "*****"; int bytesRead, bytesAvailable, bufferSize; byte[] buffer; int maxBufferSize = 1 * 1024 * 1024; String urlString = "http://mywebsite.com/uploadAudio.php"; @Override protected String doInBackground(String... params) { String uploading_file = params[0]; try { FileInputStream fileInputStream = new FileInputStream(new File(uploading_file)); URL url = new URL(urlString); conn = (HttpURLConnection) url.openConnection(); // 基础配置保持不变 conn.setDoInput(true); conn.setDoOutput(true); conn.setUseCaches(false); conn.setRequestMethod("POST"); conn.setRequestProperty("Connection", "Keep-Alive"); conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary); dos = new DataOutputStream(conn.getOutputStream()); // 1. 发送普通文本数据作为一个multipart part dos.writeBytes(twoHyphens + boundary + lineEnd); dos.writeBytes("Content-Disposition: form-data; name=\"user\"" + lineEnd); dos.writeBytes(lineEnd); // 空行分隔头和内容 // 构造并写入JSON数据(不需要URLEncoder,multipart会处理编码) JSONObject jsonObject = new JSONObject(); jsonObject.put("id", "my_data"); JSONObject data = new JSONObject(); data.put("user_data", jsonObject); String jsonStr = data.toString(); dos.writeBytes(jsonStr + lineEnd); // 2. 发送文件作为另一个multipart part dos.writeBytes(twoHyphens + boundary + lineEnd); dos.writeBytes("Content-Disposition: form-data; name=\"uploadedfile\";filename=\"" + uploading_file + "\"" + lineEnd); dos.writeBytes(lineEnd); // 写入文件内容 bytesAvailable = fileInputStream.available(); bufferSize = Math.min(bytesAvailable, maxBufferSize); buffer = new byte[bufferSize]; bytesRead = fileInputStream.read(buffer, 0, bufferSize); while (bytesRead > 0) { dos.write(buffer, 0, bufferSize); bytesAvailable = fileInputStream.available(); bufferSize = Math.min(bytesAvailable, maxBufferSize); bytesRead = fileInputStream.read(buffer, 0, bufferSize); } // 结束multipart请求 dos.writeBytes(lineEnd); dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd); // 关闭流 fileInputStream.close(); dos.flush(); dos.close(); } catch (MalformedURLException ex) { Log.e("Debug", "error: " + ex.getMessage(), ex); } catch (IOException ioe) { Log.e("Debug", "error: " + ioe.getMessage(), ioe); } catch (JSONException e) { e.printStackTrace(); } // 处理服务器响应 String line = ""; try { InputStream inputStream = conn.getInputStream(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "iso-8859-1")); if ((line = bufferedReader.readLine()) != null) { Log.e("Debug", "Server Response " + line); } bufferedReader.close(); inputStream.close(); conn.disconnect(); } catch (IOException e) { e.printStackTrace(); } return line; }
对应的PHP服务器端代码
<?php // 接收普通文本数据(JSON格式) $userData = json_decode($_POST['user'], true); if ($userData) { $userId = $userData['user_data']['id']; echo "Received user ID: " . $userId . "\n"; } // 接收上传的文件 $targetDir = "uploads/"; // 确保uploads目录存在 if (!file_exists($targetDir)) { mkdir($targetDir, 0777, true); } $targetFile = $targetDir . basename($_FILES["uploadedfile"]["name"]); if (move_uploaded_file($_FILES["uploadedfile"]["tmp_name"], $targetFile)) { echo "The file ". basename( $_FILES["uploadedfile"]["name"]). " has been uploaded."; } else { echo "Sorry, there was an error uploading your file."; } ?>
关键注意点
- 不要混合URL编码和multipart格式:multipart请求会自动处理参数的编码,不需要手动用
URLEncoder处理普通数据。 - 每个part必须用boundary分隔:每个参数(文本或文件)都要以
--boundary开头,然后是头信息、空行、内容,最后用--boundary--标记结束整个请求。 - 确保流操作顺序正确:写完所有part后一定要发送结束标记,否则服务器会认为请求不完整。
- 权限配置:Android端需要添加
INTERNET权限,针对Android 10及以下还需WRITE_EXTERNAL_STORAGE权限;PHP端要确保uploads目录有可写权限。
内容的提问来源于stack exchange,提问作者Saurabh Kumar
相关产品推荐
相关产品推荐

