关于文件上传

spring 一般使用 MultipartFile 进行文件的上传操作

@PostMapping("upload")
public Map<String, Object> uploadFile(MultipartFile file) {
    // ...
}

一般都会对操作进行鉴权,或者是对参数进行校验,例如:

String authorization = request.getHeader("Authorization");
String timestampString = request.getHeader("timestamp");
if (authorization == null || timestampString == null) {
    throw new IllegalArgumentException("验证信息未提供");
}
long timestamp = Long.parseLong(timestampString);
long currentTimeMillis = System.currentTimeMillis();
if (currentTimeMillis < timestamp) {
    throw new IllegalArgumentException("请检查客户端时间");
}
if (currentTimeMillis - timestamp > EXPIRE_MILLISECOND) {
    throw new IllegalArgumentException("验证信息超时");
}
String sign = SignatureUtil.sign(timestampString, PASSWORD);
if (!authorization.equals(sign)) {
    throw new IllegalArgumentException("验证失败");
}
  • 在 controller 层校验

    • 在 controller 层,获取到参数进行校验前,spring已经将文件流读取完毕,并存储在临时的目录下,linux中默认是在 /tmp 下某个随机目录,可以通过参数 server.tomcat.basedir 进行设置,默认的 tmp 目录可能会被系统自动清理,程序会造成找不到目录的错误。

    • 上传大文件问题 当上传的文件是一个比较大的文件时,会一直等到上传完毕,spring对参数解析完毕之后,才会交给调用者使用,可能已经消耗了较长的时间及带宽资源

  • 在拦截器 interceptor 校验

    • 在 interceptor 中,发现已经对数据进行接收完毕,只不过还没对controller层所需要的参数进行处理,在这一层,可以自行读取 request body 中的流(实际是读取临时文件的流),到controller层一般就不行了(mapping方法参数包含了获取需要获取到body体信息的参数,例如 @RequestBody / 来自body体中的参数)。此时跟在controller层一样,消耗较长的资源后,再对参数进行校验
  • 在过滤器 filer 校验

    • 在 filter 中,可以对 request 的 header / url / queryString 直接进行读取,此时 spring 还没有真正处理body体,可以在这里对参数进行必要的校验,前提是将必要的参数放在 header 或 url 上,如果试图去使用 request.getParameter("xxx")获取请求参数,则该操作会被阻塞,直至body体数据被处理完毕(接收并存入临时文件)

先校验参数再接收body

  • 在filter中操作, ServletRequest实例可能无法直接操作,强转 HttpServletRequest 即可

  • 目前已知可以直接读取的常用操作

    • ((HttpServletRequest) request).getHeader(“headerName”)
    • ((HttpServletRequest) request).getQueryString()
    • ((HttpServletRequest) request).getRequestURL()
    • ((HttpServletRequest) request.).getRequestURI()
  • 目前已知会将body体的流处理存至临时文件的操作

    • 抛异常
    • request.getParameter(“xxx”)
  • 校验成功调用 chain.doFilter(request, response);

  • 校验失败,从request获取输入流,直接关闭,再抛异常 或者 自行操作response返回信息

文档更新时间: 2019-11-26 14:40   作者:sapluk