使用Jersey上传XLS文件?

时间:2014-09-16 15:21:50

标签: java rest upload jersey xls

我使用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: 我添加了一个对IOUtils的调用,以便在xls方法中读取一些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应用程序编写的,用冷融合编写:

<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

1 个答案:

答案 0 :(得分:0)

原来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