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

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字节,均缺失末尾内容

问题根因

  1. 换行符硬编码错误:硬编码\r\n作为换行分隔符,不同操作系统换行规范存在差异,导致服务端解析multipart/form-data格式请求时无法正确识别边界,小文件可能因边界完全无法识别被直接丢弃,大文件仅能识别部分边界导致内容截断
  2. 请求头语法错误:原代码中Content-Disposition行的拼接逻辑错误,将+ lineEnd直接作为字符串拼入文件名后,导致请求头格式不符合规范,服务端解析失败
  3. 文件读写逻辑错误:原代码每次写入固定的bufferSize长度,而非实际读取到的bytesRead长度,当最后一次读取字节数小于bufferSize时,会写入无效内容或丢失实际内容
  4. 流操作顺序错误:原代码先关闭输出流再执行flush,导致缓冲区中未写入的内容直接被丢弃

修复方案

核心优化点如下:

  1. 使用System.lineSeparator()替代硬编码的\r\n,自动适配系统换行规范,保证multipart边界格式正确
  2. 修正Content-Disposition行的拼接逻辑,删除错误的字符串内容
  3. 优化文件读写逻辑,循环读取时每次写入实际读取到的bytesRead长度,避免内容丢失或无效内容写入
  4. 调整流操作顺序,先flush再关闭输出流,保证所有缓冲区内容都成功写入请求
  5. 优化boundary为复杂度更高的随机字符串,避免和文件内容重复导致边界识别错误
  6. 增加显式的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.14 13:45:04