【问题标题】:Use Jersey to upload XLS file?使用 Jersey 上传 XLS 文件?
【发布时间】:2014-11-10 10:21:32
【问题描述】:

我正在使用 Jersey 将文件上传到我的应用程序进行处理。我实现了一个可以上传 TXT 和 CSV 文件的版本。但是,当我上传 XLS 文件时,我得到了这个:

Exception in thread "Thread-12" org.jvnet.mimepull.MIMEParsingException: java.io.IOException: Stream Closed

我正在使用 ApachePOI 读取文件,但即使在 xls 方法中,如果我检查流中有多少字节可用,我会得到零。我正在测试的文件肯定有数据,并且是有效的 XLS 文件,但我不明白为什么它认为流已关闭。

我的xls方法如下:

    @POST
    @NewUpload
    @Path(FILE_PATH)
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    @Produces(MediaType.TEXT_PLAIN)
    @AccessLog
    public Response xls(
            @QueryParam(USER_PARAM)    final String userID,
            @FormDataParam(FILE_PARAM) final InputStream upload,
            @FormDataParam(FILE_PARAM) final FormDataContentDisposition detail,
            @FormDataParam(TYPE_PARAM) final String type)
    {
        try {
            System.out.println("Available bytes " + upload.available());
        } catch (IOException e) {
            System.out.println(ExceptionUtils.getFullStackTrace(e));
        }
        return upload(userID, upload, detail, type);
    } //xls

TXT 和 CSV 的方法完全相同,只是设置为不同的地址,upload(userID, upload, detail, type) 方法适当地处理不同的流。

有没有办法重置流,以便在此之后我可以正确读取它?还是有其他读取 XLS 文件的方法?

对于大文件,是否最好以另一种方式执行此操作,即从上传文件将文件保存到磁盘然后读取它?

提前致谢, 阿列克谢蓝。

编辑 1: 我在 xls 方法中添加了对 IOUtils 的调用以读取一些 Stream 并设法读取了很多字节,所以我猜 Stream 正在被其他东西关闭。

byte [] buffer = new byte[10000];
try {
    System.out.println("Bytes read " + IOUtils.read(upload, buffer));
} catch (IOException e) {
    System.out.println(ExceptionUtils.getFullStackTrace(e));
}

我将尝试创建一个单元测试来模拟正在发生的事情,这样我就可以看到流被关闭的位置。

编辑 2: 客户端写成前端web应用,用cold fusion写的:

<cfoutput>
    <legend>Upload File</legend>

    <div id ="uploadfile">
        <form class = "form-horizontal" id="upload_file_form" method="post" name="upload_file">
            <div class="control-group">
                <label class="control-label">File To Upload</label>
                <div class="controls">
                    <input id="filetoupload" class="filetoupload" type="file" name="file">
                </div>
            </div>
            <div class="control-group">
                <input type="button" 
                       value="Submit" 
                       name="submit" 
                       class="btn btn-success" 
                       id="submit" 
                       onclick="doUpload(upload_file_form);" />
            </div>
        </form>
    </div>
</cfoutput>

doUpload 脚本:

function doUpload(form) {
    var id = $('[name="id"]').val();
    var fileExtension = getFileExtension(form);

    $.ajax({
        type: "POST",
        data:  new FormData(form),
        processData: false,
        contentType: false,
        url: "/application/rest/upload/" + fileExtension + "/file/?id=" + id,
        success:function(data) {
             // get the upload id
             var uploadID = data;
             window.location ="index.cfm?controller=project&action=status&id=" + id + "&uploadID='" + uploadID + "'";
        }
    });
}

目前我正在组件测试中运行上传,这让我意识到文件的标题已被读取,但是组件测试中的文件类型为BufferedInputStream,但从客户端运行代码时的流是org.jvnet.mimepull.DataHead$ReadMultiStream 所以这可能是 mimepull 依赖的问题,有什么想法吗?

干杯, 阿列克谢蓝。

编辑 3:我想我找到了问题所在。在我的上传方法中,我在另一个线程中关闭了进程,以确保请求不会等待。 Jersey 必须在从方法返回时关闭流,并且 CSV 和 TXT 是非常简单的文件类型,它们必须在开始线程返回之前在线程中处理。我明天需要确认这一点,因为我今天没时间了,但我一定会发布结果。上传方式如下:

protected Response upload(
            final String id, 
            final InputStream input,
            final FormDataContentDisposition detail, 
            final String type) 
    {
        String id = UUIDs.timeBased().toString();

        factory
            .set(id, uploadID)
            .setFilename(detail.getFileName())
            .setType(type);

        new Thread(new Runnable() 
        {
            @Override
            public void run() 
            {
               upload.process(input); 
            } //run
        }).start();

        return Response.ok(uploadID).build();
    } //upload

【问题讨论】:

  • 您可以从上传文件的位置添加客户端部分吗?
  • 嗨@tmarwen 请参阅编辑 2 了解前端客户端和更多详细信息。我将尝试在 mimepull 库中找到 DataHead 的代码,看看发生了什么。

标签: java rest upload jersey xls


【解决方案1】:

原来 Jersey 正在关闭流,所以我不能只是启动一个新线程来将处理部分放到后台。这不是问题,因为可以通过轮询状态 URL 来获取上传的状态,因此用户仍然可以在 AJax 界面中获得响应。

新的上传方式,即无线程。

protected Response upload(
            final String id, 
            final InputStream input,
            final FormDataContentDisposition detail, 
            final String type) 
    {
        String id = UUIDs.timeBased().toString();

        factory
            .set(id, uploadID)
            .setFilename(detail.getFileName())
            .setType(type);

        upload.process(input);

        return Response.ok(uploadID).build();
    } //upload

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-29
    • 1970-01-01
    • 1970-01-01
    • 2011-11-17
    • 1970-01-01
    相关资源
    最近更新 更多