使用 java 使用 multipart/form-data 上传 zip 文件时缺少字节

Missing bytes when uploading zip file with multipart/form-data using java

我在将 .zip 文件上传到远程服务器时遇到问题,因为上传后文件中缺少一些字节。重新下载文件后,.zip 存档无法打开,这让我相信我需要这些字节才能成功执行上传。

我正在使用 multipart/form-data POST 请求上传文件。下面的代码给出了我用来执行此操作的实用程序助手 class:

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;

public class MultipartFormDataUtil {
    private final String boundary;
    private static final String lineReturn = "\r\n";
    private HttpURLConnection conn;
    private DataOutputStream dos;
    int bytesRead, bytesAvail, bufferSize;
    byte[] buffer;
    int maxBufferSize = 1*1024*1024;
    List<String> response;

    public MultipartFormDataUtil(String postUrl, LinkedHashMap<String, String> params, File file) throws IOException {
        boundary = "=-=" + System.currentTimeMillis() + "=-=";

        URL url = new URL(postUrl);
        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());

        for (String key : params.keySet()) {
            addFormPart(key, params.get(key));
        }

        addFilePart(file);

        finish();
    }

    private void addFormPart(String name, String value) throws IOException {
        dos.writeBytes("--" + boundary + lineReturn);
        dos.writeBytes("Content-Disposition: form-data; name=\"" + name + "\"" + lineReturn);
        dos.writeBytes("Content-Type: text/plain" + lineReturn + lineReturn);
        dos.writeBytes(value + lineReturn);
        dos.flush();
    }

    private void addFilePart(File file) throws IOException {
        FileInputStream fileInputStream = new FileInputStream(file);

        dos.writeBytes("--" + boundary + lineReturn);
        dos.writeBytes("Content-Disposition: form-data; name=\"file\"; filename=\"" + file.getName() + "\"" + lineReturn);
        dos.writeBytes("Content-Type: " + URLConnection.guessContentTypeFromName(file.getName()) + lineReturn);
        dos.writeBytes("Content-Transfer-Encoding: binary" + lineReturn + lineReturn);

        bytesAvail = fileInputStream.available();
        bufferSize = Math.min(bytesAvail, maxBufferSize);
        buffer = new byte[bufferSize];

        bytesRead = fileInputStream.read(buffer, 0, bufferSize);

        while (bytesRead > 0) {
            dos.write(buffer, 0, bufferSize);
            bytesAvail = fileInputStream.available();
            bufferSize = Math.min(bytesAvail, maxBufferSize);
            bytesRead = fileInputStream.read(buffer, 0, bufferSize);
        }
        dos.flush();

        dos.writeBytes(lineReturn);
        dos.flush();
        fileInputStream.close();
    }

    private void finish() throws IOException {
        response = new ArrayList<String>();

        dos.writeBytes("--" + boundary + "--" + lineReturn);
        dos.flush();
        dos.close();

        BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        String line;

        while ((line = reader.readLine()) != null) {
            response.add(line);
        }

        reader.close();
        conn.disconnect();
    }

    public List<String> getResponse() {
        return response;
    }

为了在信用到期时给予信用,此实用程序基于 Peter's Notes and CodeJava.net 中的示例。此实用程序使用以下代码调用:

protected static void postFile(String url, LinkedHashMap<String, String> params, File file) throws Exception {
    try {
        MultipartFormDataUtil multipartRequest = new MultipartFormDataUtil(url, params, file);
        List<String> response = multipartRequest.getResponse();

        for (String line : response) {
            System.out.println(line);
        }

    } catch (IOException ioe) {
        log.warn("There was an error posting the file and form data", ioe);
    }
}

在这种情况下,上传 url 是到 Amazon S3 存储桶,然后将其传递到目标系统。正是在这个最终目的地,我可以看到应该在 .zip 文件上 运行ning 的过程失败了(注意:该过程是由 Rails 应用程序 运行并给出错误 "Error identifying package type: can't dup NilClass")。下载文件后,我看到文件大小为 3,110,416 字节,而不是 3,110,466 字节。我无法再提取存档以查看其中的内容; mac 存档实用程序以 "Error 2 - No such file or directory".

响应

我缺乏这方面的概念背景,无法了解流程中哪里出了问题。我希望有人能够告诉我我在实用程序中犯了一个错误 class,或者让我知道是其他问题。

感谢您提供的任何见解,如果我能post有任何其他帮助,请告诉我。

编辑:我收集了一些关于不同大小的文件上传(以字节为单位)的额外信息:

原创---------已上传---------差异

10,167,389______10,167,238______151

3,110,466_______3,110,416_______50

156,885_________156,885_________0

95,639,352______95,637,925______1,427

对于上传后丢失字节的 3 个文件,每个文件丢失的总数据百分比约为(但不完全)0.0015%,但彼此不相等。

经过进一步研究,我们发现该错误与此问题中显示的 multipart/form-data 实用程序没有任何关系。相反,它与我们在 here 中的文件下载客户端有关。我们没有将 FileTransfer 客户端设置为以二进制形式下载文件,这对于 .zip 文件是必需的。

为了您在 java 中的 multipart/form-data 目的,请随意使用原始问题中包含的代码 - 假设您的原始文件没有问题,它会很好用。