使用Jersey上传文件时设置文件大小限制

14

我目前正在使用Jersey Rest实现文件上传功能。我想设置一个最大允许的文件大小,这似乎是一个非常普遍的需求。

我的第一种方法是使用Jerseys FormDataContentDisposition,它应该包含我可能需要的有关文件的所有信息。但是除了文件名之外,似乎缺少所有信息,包括文件大小。

这是我的rest方法:

@POST
@Path("uploadFile/")
@Consumes("multipart/form-data")
@Produces("text/html")
public String handleDocumentUpload(
    @FormDataParam("file") InputStream uploadedInputStream,
    @FormDataParam("file") FormDataContentDisposition fileDetail)
{
    if(fileDetail.getSize() > MAX_FILE_SIZE)
    {
        throw new IllegalArgumentException(
                "File is to big! Max size is " + MAX_FILE_SIZE);
    }
    // ...more file handling logic
}

由于返回的大小总是“-1”,所以它不起作用!

我使用一个非常简单的HTML表单进行文件上传:

<html>
  <head>
    <title>File upload</title>
  </head>
  <body>
 <p>Choose file</p>
 <form enctype="multipart/form-data" method="POST" action="uploadFile">
   <input type="file" name="file" size="50">
   <input type="submit" value="Upload">
 </form>
  </body>
</html>

那么现在来谈我的问题;如何使用jersey强制执行文件大小限制?必须有一种简单的方法,而不必诉诸于将整个文件读入内存(ByteArray),然后获取实际大小,对吧?


只是为了澄清,除了无法限制文件大小之外,它的工作非常出色。 - mattias_avelin
目前针对 jersey 2,以下文章解决了该问题: https://dev59.com/Ym015IYBdhLWcg3w7wbF#25848569 - Indraneel
5个回答

8
如果客户端没有发送文件大小,就从流中读取文件。一旦达到大小限制,停止读取并拒绝该文件。无论如何,您应该这样做,因为您不能信任客户端(任何人都可以创建一个应用程序向您的服务发送http请求,而这些请求可能没有您所期望的正确数据——因此必须考虑到这一点)。
此外,还可以在Web表单中添加一些验证以快速失败,但我不是JavaScript专家,因此不确定是否/如何进行。

谢谢Martin!你当然是对的,我不应该指望客户提供正确的文件大小。我们实现了这样一个功能:如果客户端声明的文件大小> MAX_SIZE,则我们会“快速失败”,否则我们会读取流直到达到MAX_SIZE。我们没有找到在客户端验证文件大小的简单方法,所以只能这样做。再次感谢您的建议! - mattias_avelin
为了防止您的API收到不必要的HTTP调用,您可以创建一个CORS过滤器。 - joninx

6
如果您正在使用Tomcat,您可以设置文件大小阈值,以便将文件写入磁盘。请根据您的机器设置合理的值。
例如,如果servlet在web.xml中:
<servlet>
  <servlet-name>Upload Servlet</servlet-name>
  <servlet-class>YourServletName</servlet-class>

  <multipart-config>
   <!-- 10MB of files -->
   <max-file-size>10485760</max-file-size>
   <!-- 10KB of form data -->
   <max-request-size>10240</max-request-size>
   <!-- Buffer to disk over 512KB -->
   <file-size-threshold>524288</file-size-threshold>
 </multipart-config>

</servlet>

或者使用注释:
@MultipartConfig(
    maxFileSize=10485760,     // 10Mb max
    fileSizeThreshold=524288, //512 Kb before buffering to disk
    maxRequestSize=10240      // 10Kb of meta data
    location=/tmp             // with permission to write, default uses tomcat tmp
)

关于Tomcat中HttpRequest的最大允许大小的问题:


根据https://docs.oracle.com/javaee/7/tutorial/servlets011.htm,我认为max-file-size只适用于单个文件,而max-request-size适用于整个请求(因此可能涉及多个文件和多部分)。 - Ondrej Burkert
1
MultipartConfig并不适用于JAX-RS/Jersey,因为它是为Servlet而非JAX-RS资源设计的。在Jersey中没有与MultipartConfig相对应的内容。请参考https://jersey.github.io/documentation/latest/media.html#multipart以获取详细信息。这里没有快速失败的解决方案。我们不能依赖Content-Length头,必须检查文件大小,并在超过预设阈值时拒绝上传。为什么呢?想象一下正在上传一个大文件。Jersey首先必须将整个文件存储到磁盘上,这不仅耗费时间,而且如果服务器的磁盘空间有限,也是不安全的。 - Viswanath

3
您可以使用以下代码检查请求正文的长度(以字节为单位),并通过输入流使其可用:
public Response uploadFile(@Context final HttpServletRequest request, @FormDataParam("uploadFile") InputStream uploadedInputStream,
      @FormDataParam("uploadFile") FormDataContentDisposition fileDetail, @FormDataParam("uploadFile") FormDataBodyPart body) {

关键部分是@Context final HttpServletRequest request。然后在方法体中,您可以获取输入流的长度,并根据需要做出反应:

int contentLength = request.getContentLength();

if (contentLength == -1 || contentLength > MAX_REQUEST_SIZE) {
  // deal with it
}

在这种情况下,假设您想要限制文件大小超过1GB,因此一旦我们上传整个文件,您的验证就会出现在图片中。所以它非常缓慢,不可接受。您考虑过这种情况吗? - Jayesh Dhandha

1

你可以通过读取请求头来获取请求大小。 在你的示例中:

@POST
@Path("uploadFile/")
@Consumes("multipart/form-data")
@Produces("text/html")
public String handleDocumentUpload(
    @HeaderParam("content-length") long contentLength,
    @FormDataParam("file") InputStream uploadedInputStream,
    @FormDataParam("file") FormDataContentDisposition fileDetail) {

    if(contentLength > MAX_FILE_SIZE) {
      throw new IllegalArgumentException(
            "File is to big! Max size is " + MAX_FILE_SIZE);
    }
  // ...more file handling logic
}

3
内容长度可以伪造。 - Somaiah Kumbera

0
你可以编写自定义的 class LimitedSizeInputStream extends InputStream 类,并使用@Override read方法检查特定大小限制,如https://dev59.com/7WUp5IYBdhLWcg3wLlVV#30072143所述。将你的 InputStream 包装在 new LimitedSizeInputStream(fileStream, FILE_SIZE_LIMIT)中,当达到读取限制时,就会抛出异常。

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