HttpURLConnection上传文件不完整:小文件丢失/大文件截断问题及解决
文件上传异常问题排查与修复
原实现代码
前端Java上传代码
public int uploadFile(String sourceFileUri) { String fileName = sourceFileUri; HttpURLConnection conn; DataOutputStream dos; String lineEnd = "\r\n"; String twoHyphens = "--"; String boundary = "*****"; int bytesRead, bytesAvailable, bufferSize; byte[] buffer; int maxBufferSize = 1*1024*1024; File sourceFile = new File(sourceFileUri); if (!sourceFile.isFile()) { dialog.dismiss(); Log.e("uploadFile", "Source File not exist :" +uploadFilePath + "" + uploadFileName); runOnUiThread(new Runnable() { public void run() { messageText.setText("Source File not exist :" +uploadFilePath + "" + uploadFileName); } }); return 0; } else { try { // open a URL connection to the Servlet FileInputStream fileInputStream = new FileInputStream(sourceFile); URL url = new URL(upLoadServerUri); // Open a HTTP connection to the URL conn = (HttpURLConnection) url.openConnection(); conn.setDoInput(true); // Allow Inputs conn.setDoOutput(true); // Allow Outputs conn.setUseCaches(false); // Don't use a Cached Copy conn.setRequestMethod("POST"); conn.setRequestProperty("Connection", "Keep-Alive"); conn.setRequestProperty("ENCTYPE", "multipart/form-data"); conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary); conn.setRequestProperty("uploaded_file", fileName); dos = new DataOutputStream(conn.getOutputStream()); dos.writeBytes(twoHyphens + boundary + lineEnd); dos.writeBytes("Content-Disposition: form-data; name=uploaded_file;filename=" + fileName + " + lineEnd"); dos.writeBytes(lineEnd); // create a buffer of maximum size bytesAvailable = fileInputStream.available(); bufferSize = Math.min(bytesAvailable, maxBufferSize); buffer = new byte[bufferSize]; // read file and write it into form... 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); } // send multipart form data necessary after file data... dos.writeBytes(lineEnd); dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd); // Responses from the server (code and message) serverResponseCode = conn.getResponseCode(); String serverResponseMessage = conn.getResponseMessage(); Log.i("uploadFile", "HTTP Response is : " + serverResponseMessage + ": " + serverResponseCode); if(serverResponseCode == 200){ runOnUiThread(new Runnable() { public void run() { String msg = "File Upload Completed.\n\n " +uploadFileName; messageText.setText(msg); Toast.makeText(UploadToServer.this, "File Upload Complete.", Toast.LENGTH_SHORT).show(); } }); } //close the streams // fileInputStream.close(); dos.flush(); dos.close(); } catch (MalformedURLException ex) { dialog.dismiss(); ex.printStackTrace(); runOnUiThread(new Runnable() { public void run() { messageText.setText("MalformedURLException Exception : check script url."); Toast.makeText(UploadToServer.this, "MalformedURLException", Toast.LENGTH_SHORT).show(); } }); Log.e("Upload file to server", "error: " + ex.getMessage(), ex); } catch (Exception e) { dialog.dismiss(); e.printStackTrace(); runOnUiThread(new Runnable() { public void run() { messageText.setText("Got Exception : see logcat "); Toast.makeText(UploadToServer.this, "Got Exception : see logcat ", Toast.LENGTH_SHORT).show(); } }); Log.e("Upload file Exception", "Exception : " + e.getMessage(), e); } dialog.dismiss(); return serverResponseCode; } // End else block }
后端PHP接收代码
$file_path = "uploads/files/"; $file_path = $file_path . basename( $_FILES['uploaded_file']['name']); if(move_uploaded_file($_FILES['uploaded_file']['tmp_name'], $file_path)) { echo "success"; }else{ echo "fail"; }
异常表现
- 10-1000字节的小文件无法上传:服务端返回正常但文件并未实际保存
- 大文件上传后内容被截断:如3925字节的文件上传后仅保存1002字节,26216字节的文件上传后仅保存26173字节,均缺失末尾内容
问题根因
- 换行符硬编码错误:硬编码
\r\n作为换行分隔符,不同操作系统换行规范存在差异,导致服务端解析multipart/form-data格式请求时无法正确识别边界,小文件可能因边界完全无法识别被直接丢弃,大文件仅能识别部分边界导致内容截断 - 请求头语法错误:原代码中Content-Disposition行的拼接逻辑错误,将
+ lineEnd直接作为字符串拼入文件名后,导致请求头格式不符合规范,服务端解析失败 - 文件读写逻辑错误:原代码每次写入固定的bufferSize长度,而非实际读取到的bytesRead长度,当最后一次读取字节数小于bufferSize时,会写入无效内容或丢失实际内容
- 流操作顺序错误:原代码先关闭输出流再执行flush,导致缓冲区中未写入的内容直接被丢弃
修复方案
核心优化点如下:
- 使用
System.lineSeparator()替代硬编码的\r\n,自动适配系统换行规范,保证multipart边界格式正确 - 修正Content-Disposition行的拼接逻辑,删除错误的字符串内容
- 优化文件读写逻辑,循环读取时每次写入实际读取到的bytesRead长度,避免内容丢失或无效内容写入
- 调整流操作顺序,先flush再关闭输出流,保证所有缓冲区内容都成功写入请求
- 优化boundary为复杂度更高的随机字符串,避免和文件内容重复导致边界识别错误
- 增加显式的
conn.connect()调用,确保HTTP连接建立完成后再传输数据
修复后完整Java代码
public int uploadFile(String sourceFileUri) { String fileName = sourceFileUri; HttpURLConnection conn; DataOutputStream dos; String lineEnd = "\r\n"; String twoHyphens = "--"; //String boundary = "*****file_Antares*****"; String boundary = "WebKitFormBoundary7$G67whGfe341h6@fr561"; int bytesRead, bytesAvailable, bufferSize; byte[] buffer; int maxBufferSize = 8*1024; File sourceFile = new File(sourceFileUri); if (!sourceFile.isFile()) { dialog.dismiss(); Log.e("uploadFile", "Source File not exist :" +uploadFilePath + "" + uploadFileName); runOnUiThread(new Runnable() { public void run() { messageText.setText("Source File not exist :" +uploadFilePath + "" + uploadFileName); } }); return 0; } else { try { // open a URL connection to the Servlet FileInputStream fileInputStream = new FileInputStream(sourceFile); URL url = new URL(upLoadServerUri); // Open a HTTP connection to the URL conn = (HttpsURLConnection) url.openConnection(); conn.setDoInput(true); // Allow Inputs conn.setDoOutput(true); // Allow Outputs conn.setUseCaches(false); // Don't use a Cached Copy conn.setRequestMethod("POST"); conn.setRequestProperty("Connection", "Keep-Alive"); conn.setRequestProperty("enctype", "multipart/form-data"); conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary); conn.setRequestProperty("uploaded_file", fileName); conn.connect(); //new!!! dos = new DataOutputStream(conn.getOutputStream()); dos.writeBytes(twoHyphens + boundary + System.lineSeparator());// add new System.lineSeparator()) dos.writeBytes("Content-Disposition: form-data; name=uploaded_file;filename=" + fileName + System.lineSeparator()); dos.writeBytes(System.lineSeparator()); // create a buffer of maximum size bytesAvailable = fileInputStream.available(); bufferSize = Math.min(bytesAvailable, maxBufferSize); buffer = new byte[bufferSize]; while ((bytesRead = fileInputStream.read(buffer)) >= 0) { dos.write(buffer, 0, bytesRead); } // send multipart form data necessary after file data... dos.writeBytes(System.lineSeparator()); dos.writeBytes(twoHyphens + boundary + twoHyphens + System.lineSeparator()); //close the streams // dos.close(); dos.flush(); fileInputStream.close(); // Responses from the server (code and message) serverResponseCode = conn.getResponseCode(); String serverResponseMessage = conn.getResponseMessage(); Log.i("uploadFile", "HTTP Response is : " + serverResponseMessage + ": " + serverResponseCode); if(serverResponseCode == 200){ runOnUiThread(new Runnable() { public void run() { String msg = "File Upload Completed.\n\n See uploaded file here : \n\n" +uploadFileName; messageText.setText(msg); Toast.makeText(UploadToServer.this, "File Upload Complete.", Toast.LENGTH_SHORT).show(); } }); } } catch (MalformedURLException ex) { dialog.dismiss(); ex.printStackTrace(); runOnUiThread(new Runnable() { public void run() { messageText.setText("MalformedURLException Exception : check script url."); Toast.makeText(UploadToServer.this, "MalformedURLException", Toast.LENGTH_SHORT).show(); } }); Log.e("Upload file to server", "error: " + ex.getMessage(), ex); } catch (Exception e) { dialog.dismiss(); e.printStackTrace(); runOnUiThread(new Runnable() { public void run() { messageText.setText("Got Exception : see logcat "); Toast.makeText(UploadToServer.this, "Got Exception : see logcat ", Toast.LENGTH_SHORT).show(); } }); Log.e("Upload file Exception", "Exception : " + e.getMessage(), e); } dialog.dismiss(); return serverResponseCode; } // End else block }
内容的提问来源于stack exchange,提问作者Alex Dem
相关产品推荐
相关产品推荐

