【问题标题】:How could I upload a large file in chunks using Java?如何使用 Java 分块上传大文件?
【发布时间】:2011-08-29 19:00:39
【问题描述】:

我需要使用 Java 分块上传一个大文件。

有什么示例代码可以参考吗?

【问题讨论】:

  • 只需在HttpURLConnection上设置分块传输模式。

标签: java file file-upload


【解决方案1】:

*可以使用plupload来完成。 这是样本。 我的 index.html 如下:-

    <!DOCTYPE html>
    <html>
    <head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
    <title>Upload</title>
    <!-- production -->
    <script type="text/javascript" src="js/plupload.full.min.js"></script>
    <!-- debug 
    <script type="text/javascript" src="../js/moxie.js"></script>
    <script type="text/javascript" src="../js/plupload.dev.js"></script>
    -->
    </head>
    <body style="font: 13px Verdana; background: #eee; color: #333">
    <div id="filelist"></div>
    <br />
        <button id="pickfiles" >Select file</button> 
        <button id="uploadfiles" >Upload</button>
        <div id="container">
    </div>
    <br />
    <pre id="console"></pre>
    <script type="text/javascript">
    // Custom example logic
    var uploader = new plupload.Uploader({
        runtimes : 'html5',
        browse_button : 'pickfiles', // you can pass an id...
        container: document.getElementById('container'), // ... or DOM Element itself
        url : 'UploadAction',//upload.php
        chunk_size : '1mb',
        method:'POST',
        flash_swf_url : 'js/Moxie.swf',
        silverlight_xap_url : 'js/Moxie.xap',

        filters : {
            max_file_size : '100gb',
            mime_types: [
                {title : "Image files", extensions : "jpg,gif,png"},
                {title : "Zip files", extensions : "zip,txt,vmdk"}
            ]
        },
        init: {
            PostInit: function() {
                document.getElementById('filelist').innerHTML = '';
                document.getElementById('uploadfiles').onclick = function() {
                    uploader.start();
                    return false;
                };
            },
            FilesAdded: function(up, files) {
                plupload.each(files, function(file) {
                    document.getElementById('filelist').innerHTML += '<div id="' + file.id + '">' + file.name + ' (' + plupload.formatSize(file.size) + ') <b></b></div>';
                });
            },
            UploadProgress: function(up, file) {
                document.getElementById(file.id).getElementsByTagName('b')[0].innerHTML = '<span>' + file.percent + "%</span>";
            },
            Error: function(up, err) {
                document.getElementById('console').appendChild(document.createTextNode("\nError #" + err.code + ": " + err.message));
            }
        }
    });
    uploader.init();
    </script>
    </body>
    </html>
<!-- end snippet -->

我的java后端代码(Servlet)如下:-

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.util.Streams;
public class UploadAction extends HttpServlet {
    private static final long serialVersionUID = 3447685998419256747L;
    private static final String RESP_SUCCESS = "{\"jsonrpc\" : \"2.0\", \"result\" : \"success\", \"id\" : \"id\"}";
    private static final String RESP_ERROR = "{\"jsonrpc\" : \"2.0\", \"error\" : {\"code\": 101, \"message\": \"Failed to open input stream.\"}, \"id\" : \"id\"}";
    public static final String JSON = "application/json";
    public static final int BUF_SIZE = 2 * 1024;
    public static final String FileDir = "/home/asjha/uploads/";

    private int chunk;
    private int chunks;
    private String name;
    private String user;
    private String time;
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String responseString = RESP_SUCCESS;
        boolean isMultipart = ServletFileUpload.isMultipartContent(req);

        if(isMultipart){
            ServletFileUpload upload = new ServletFileUpload();
            try {
                FileItemIterator iter = upload.getItemIterator(req);
                while (iter.hasNext()) {
                    FileItemStream item = iter.next();
                    InputStream input = item.openStream();
                    // Handle a form field.
                    if(item.isFormField()){
                        String fileName = item.getFieldName();
                        String value = Streams.asString(input);
                        if("name".equals(fileName)){
                            this.name = value;
                        }else if("chunks".equals(fileName)){
                            this.chunks = Integer.parseInt(value);
                        }else if("chunk".equals(fileName)){
                            this.chunk = Integer.parseInt(value);
                        }else if("user".equals(fileName)){
                            this.user = value;
                        }else if("time".equals(fileName)){
                            this.time = value;
                        }
                    }

                    // Handle a multi-part MIME encoded file.
                    else {
                        File dstFile = new File(FileDir);
                        if (!dstFile.exists()){
                            dstFile.mkdirs();
                        }

                        File dst = new File(dstFile.getPath()+ "/" + this.name);

                        saveUploadFile(input, dst);
                    }
                }
            }
            catch (Exception e) {
                responseString = RESP_ERROR;
                e.printStackTrace();
            }
        }

        // Not a multi-part MIME request.
        else {
            responseString = RESP_ERROR;
        }

        if(this.chunk == this.chunks - 1){
            System.out.println("name"+this.name);
        }
        resp.setContentType(JSON);
        byte[] responseBytes = responseString.getBytes();
        resp.setContentLength(responseBytes.length);
        ServletOutputStream output = resp.getOutputStream();
        output.write(responseBytes);
        output.flush();
    }
    private void saveUploadFile(InputStream input, File dst) throws IOException {
        OutputStream out = null;
        try {
            if (dst.exists()) {
                out = new BufferedOutputStream(new FileOutputStream(dst, true),
                        BUF_SIZE);
            } else {
                out = new BufferedOutputStream(new FileOutputStream(dst),
                        BUF_SIZE);
            }
            byte[] buffer = new byte[BUF_SIZE];
            int len = 0;
            while ((len = input.read(buffer)) > 0) {
                out.write(buffer, 0, len);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != input) {
                try {
                    input.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != out) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

详情请参考 plupload,在 github 上您可以看到 jakobadam 和 rocky 的示例项目。

如果需要上传多个文件,请告诉我。使用 plupload 我们可以上传任意数量的任意大小的文件。此示例适用于非常大的单个文件上传。 不要忘记包含 plupload.full.min.js。希望这有助于*强调文本**

【讨论】:

  • Apache commons 如果我们没有像 nginx 等任何超时问题也可以使用。Plupload 提供了许多使用 flash、applet 等上传的方法。
【解决方案2】:

以下是使用块上传文件的原生 Java 代码示例:

final String       LF = "\r\n"; // Line separator required by multipart/form-data. Can be static class constant
final String boundary = Long.toHexString(System.currentTimeMillis()); // Just generate some unique random value.

HttpURLConnection connection = (HttpURLConnection) new URL("http://some.com/upload").openConnection();
try {
    connection.setDoOutput(true);
    connection.setChunkedStreamingMode(4096);
    connection.setRequestMethod("POST");
    connection.addRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
    connection.addRequestProperty("Accept", "application/json");
    connection.addRequestProperty("Authorization", myToken);
    try (OutputStream os = connection.getOutputStream();
            Writer writer = new OutputStreamWriter(os, StandardCharsets.UTF_8)) {
        writer.append("--").append(boundary).append(LF);
        writer.append("Content-Disposition: form-data; name=\"dataFile\"; filename=\"file.zip\"").append(LF);
        writer.append("Content-Type: application/zip").append(LF);
        writer.append(LF);
        writer.flush();

        // Write body
        writeBinaryBody(os);
        writer.append(LF).append("--").append(boundary).append("--").append(LF);
        writer.flush();
        os.flush();
    }
    if (200 != connection.getResponseCode()) {
        try (Reader reader = new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8)) {
            // Handle error here
        }
    }
} finally {
    connection.disconnect();
}

本例中的代码是基于this answer关于从java上传http文件的。不同之处在于对 connection.setChunkedStreamingMode(4096); 的调用定义了应该使用分块流。

【讨论】:

  • @Tunelt 我认为这是一个完全不同的问题。请发布一个新问题。我没有答案,但如果有解决方案我会很感兴趣。
  • 我正在尝试使用此解决方案,但出现网络错误。可能的原因是该解决方案中缺少 writeBinaryBody() 方法的实现。您能否编辑并完成此解决方案以在块中写入文件?
【解决方案3】:

您可以简单地自己分解文件,使用Socket API 发送它,然后重新组装文件。

【讨论】:

    【解决方案4】:

    试试Apache Commons upload。它支持流媒体,可能适合你。

    【讨论】:

      【解决方案5】:

      使用 RandomAccessFile。我相信这已经在 SO 上进行了介绍。

      java file input with rewind()/reset() capability

      基本上,您只需寻找起点,从那里写入任意数量的字节,并记住您停止写入的点。

      【讨论】:

      • 嗯?这是完全没有必要的。你不需要做任何这些。顺便说一句,我认为问题是询问客户端而不是服务器端。
      • @Robin 我猜 OP 会做出决定,但我可能误解了这个问题。
      猜你喜欢
      • 2011-07-10
      • 1970-01-01
      • 2020-05-15
      • 2015-11-30
      • 1970-01-01
      • 1970-01-01
      • 2018-01-20
      • 1970-01-01
      • 2022-08-16
      相关资源
      最近更新 更多