Spring Rest:通过Rest Controller上传大文件(1GB)

4

[背景:Java 8,Spring Boot 1.5.1]

我们正在创建一个RESTful服务,需要能够上传大文件。我想要的是一个API,它应该长这个样子:

@RequestLine("POST /object")
@Headers("Content-Type: multipart/form-data")
void create(InputStreamResource resource, ...);

我不想使用MultipartFile,因为这样会读取整个文件,也不想使用HttpServletRequest,因为它似乎并不适合此处,而需要某种类型的HttpMessageConverter。话虽如此,使用HttpServletRequest可以按照以下方式解决问题。

ServletFileUpload upload = new ServletFileUpload();
FileItemIterator iterator = upload.getItemIterator(request);
while (iterator.hasNext()) {
  FileItemStream e = iterator.next();
  if (!e.isFormField()) {
    InputStream inputStream = item.openStream();
    //...
  }
}

理想情况下,我希望有一个InputStream或者类似的东西来包装它,比如上面展示的InputStreamResource
如果您有任何建议,将不胜感激。
1个回答

1

我用这种方式使它工作起来了,似乎这是处理上传的唯一方法,而不需要使用临时文件或读取文件到字节数组中使用@RequestBody注释,正如在stackoverflow.com上的一个线程中建议的那样:

@RequestMapping("firmware/{firmwareId:\\d+}/logs/upload")
public
@ResponseBody
ResponseEntity handleUploadLogs(@PathVariable("firmwareId") final Long firmwareId, final HttpServletRequest request) {
    try (final InputStream is = getInputStream(request)) {
        if (is != null) {
            firmwareContentService.uploadLogs(firmwareId, is);

            return ok().build();
        } else {
            return badRequest().build();
        }
    } catch (IOException | FileUploadException e) {
        return badRequest().build();
    }
}

private InputStream getInputStream(final HttpServletRequest request) throws IOException, FileUploadException {
    final ServletFileUpload upload = new ServletFileUpload();
    final FileItemIterator iterator = upload.getItemIterator(request);

    InputStream is = null;

    while (iterator.hasNext()) {
        final FileItemStream item = iterator.next();

        if (!item.isFormField()) {
            is = item.openStream();

            break;
        }
    }

    return is;
}

更新

这段简单的代码也适用于我。只需将InputStream放入方法参数中:

@PostMapping("firmware/{firmwareId:\\d+}/logs/upload")
public
@ResponseBody
ResponseEntity handleUploadLogs(@PathVariable("firmwareId") final Long firmwareId, final InputStream is) {
    try {
        firmwareContentService.uploadLogs(firmwareId, is);

        return ok().build();
    } catch (IOException e) {
        return badRequest().build();
    }
}

上面的“简单代码”实际上并没有起作用。方法参数中的InputStream包含整个HTTP请求。

谢谢,伊戈尔。不幸的是,你的解决方案已经在我原始帖子中描述过了(如果不清楚,我道歉)。我的帖子要点是我不想要一个HttpServletRequest(或者MultipartFile),例如ResponseEntity<Void> handleUploadLogs(final HttpServletRequest request),因为我想要一个干净的API,并且更希望有一个包装了它的InputStream或POJO。 - user3971703
例如 - 如果我们考虑倒数的情况,下载文件 - 我不需要传递 HttpServletResponse 来写入其 OutputStream,我只需要返回一个 ResponseEntity<Resource> 就可以流式传输所请求的对象。 - user3971703
只需将InputStream放入方法参数中即可。不知何故,这对我起作用了。 - Igor Bljahhin
1
对不起,Igor,在我接受之前必须进行更多测试,现在似乎我说得太早了 :( ... Spring 在方法调用时传递的 InputStream [A] 的内容与您自己使用 Java API [B] 读取文件时不同。例如,拿一个小图像,将两个流[A和B]都读取到字节数组中并比较其内容。此外,当我将内容[A]保存到磁盘时,我无法将其打开为图像文件。你明白吗? - user3971703
你是否正在导入badRequest.build()和ok.build()的静态代码?如果是,那么这是你可以分享的代码吗? - Kent Bull
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接