相关推荐recommended
SpringBoot:使用applicationoctet-stream 流式上传大文件,解决Mutipartfile multipartform-data上传产生临时文件问题
作者:mmseoamin日期:2023-12-05

目录

  • multipart原理简介
    • multipart的流是服务器临时文件流
    • multipartfile生成临时文件到默认文件夹
    • 临时文件的目录可配
    • Mutipartfile的文件默认来自临时文件
    • Mutipartfile产生临时文件的好处坏处
      • 好处
      • 坏处
      • 纯流式上传application/octet-stream

        multipart原理简介

        multipart的流是服务器临时文件流

        multipart的流不是HTTPRequest的流,而是服务器的临时文件

        multipart/form-data是表单上传,接口可以从Mutipartfile对象获取输入流getInputStream,表面上看没产生临时文件

        org.springframework.web.multipart.support.StandardMultipartHttpServletRequest的getInputStream

        		@Override
        		public InputStream getInputStream() throws IOException {
        			return this.part.getInputStream();
        		}
        

        这里面的流是从DiskFileItem(一眼磁盘)拿的,还告诉了你临时目录位置location

        SpringBoot:使用applicationoctet-stream 流式上传大文件,解决Mutipartfile multipartform-data上传产生临时文件问题,在这里插入图片描述,第1张

        multipartfile生成临时文件到默认文件夹

        从原理上看,在tomcat源码的org.apache.catalina.connector.Request类中,parseParts方法就将multipart格式的请求中文件生成到临时文件夹

        			File location;
                    String locationStr = mce.getLocation();
                    if (locationStr == null || locationStr.length() == 0) {
                        location = ((File) context.getServletContext().getAttribute(
                                ServletContext.TEMPDIR));
                    } else {
                        // If relative, it is relative to TEMPDIR
                        location = new File(locationStr);
                        if (!location.isAbsolute()) {
                            location = new File(
                                    (File) context.getServletContext().getAttribute(ServletContext.TEMPDIR),
                                    locationStr).getAbsoluteFile();
                        }
                    }
        

        临时文件的目录可配

        这个location就是javax.servlet.MultipartConfigElement的配置,可以通过spring.servlet.multipart系列配置的location指定临时文件目录,这个临时文件用完,也会被自动清除

            private final String location;// = "";
            private final long maxFileSize;// = -1;
            private final long maxRequestSize;// = -1;
            private final int fileSizeThreshold;// = 0;
        

        Mutipartfile的文件默认来自临时文件

        Mutipartfile的注释中说明了,文件是stored in memory or temporarily on disk,存在内存或磁盘临时文件的,所以上传大文件时,会发现前端进度条走完了,代码才走进自己写得接口代码

        /**
         * A representation of an uploaded file received in a multipart request.
         *
         * 

        The file contents are either stored in memory or temporarily on disk. * In either case, the user is responsible for copying file contents to a * session-level or persistent store as and if desired. The temporary storage * will be cleared at the end of request processing. * * @author Juergen Hoeller * @author Trevor D. Cook * @since 29.09.2003 * @see org.springframework.web.multipart.MultipartHttpServletRequest * @see org.springframework.web.multipart.MultipartResolver */ public interface MultipartFile extends InputStreamSource {

        Mutipartfile产生临时文件的好处坏处

        好处

        帮开发封装了上传逻辑

        可以在此基础实现切片上传到本地最后合并

        坏处

        对我来说

        文件转储时间翻倍:我的文件存储服务器不是接收请求的服务所在服务器,我还得把文件转存到文件服务器,而服务到文件服务器还得再传一遍,时间基本double

        前端进度不真实:前端显示的上传进度仅是浏览器给到服务的进度

        占用资源:临时文件多少占点服务器磁盘空间

        纯流式上传application/octet-stream

        一段半伪代码,application/octet-stream参数体只能有文件,参数通过放headers里给就可以解决这个问题

            @PostMapping(value = "/upload", headers = "content-type=application/octet-stream;charset=utf-8")
            public Object upload(HttpServletRequest request,
                                                 @RequestHeader("userId") String userId,
                                                 @RequestHeader("fileName") String fileName,
                                                 @RequestHeader("fileSize") String fileSize,
                                                 @RequestHeader("dirId") String dirId) {
                try (InputStream in = request.getInputStream()) {
                    fileName = URLDecoder.decode(fileName,"utf-8");
                    uploadFileServer(in, fileName, fileSize, userId, dirId);
                    return "success";
                } catch (Exception e) {
                    log.error("上传报错", e);
                    return "error";
                }
            }