文件上传

发布时间 2024-01-04 20:07:25作者: zhegeMaw

本文所有内容都是流式上传(数据通过body传输)对于本地文件上传(传入本地路径,服务端根据路径读取文件,比较简单)不做讨论。

 

文件上传可以分为普通上传、表单上传、分片上传。

 

1. 普通上传

直接在body传输文件的字节流,可以在header中传递业务参数。

  • 仅支持单文件上传

  • Content-Type为文件类型,如audio/mpeg

  • // GPT生成,仅供参考
    import
    java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/upload") public class FileUploadServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 获取请求头中的参数 String fileName = request.getHeader("filename"); // 获取请求体中的文件内容 InputStream inputStream = request.getInputStream(); // 保存文件到指定路径 String uploadPath = "path/to/upload/directory"; File uploadDir = new File(uploadPath); if (!uploadDir.exists()) { uploadDir.mkdirs(); } String filePath = uploadPath + File.separator + fileName; try (OutputStream outputStream = new FileOutputStream(filePath)) { byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); } } // 返回成功响应 response.setStatus(HttpServletResponse.SC_OK); response.getWriter().write("上传成功"); } }

     

2. 表单上传(推荐)

使用multipart/form-data类型上传文件

  • 支持单文件、多文件

  • 后端处理方式可以为:原生HttpServletRequest、MultipartFile、FileUpload

  • Content-Type为multipart/form-data

  • 支持同时传递参数、文件(不宜过大)

  • // 使用原生HttpServletRequest处理上传文件
    import
    java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import javax.servlet.ServletException; import javax.servlet.annotation.MultipartConfig; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.Part; @WebServlet("/upload") @MultipartConfig public class FileUploadServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 获取上传的文件 Part filePart = request.getPart("file"); String fileName = getFileName(filePart); // 保存文件到指定路径 String uploadPath = "path/to/upload/directory"; File uploadDir = new File(uploadPath); if (!uploadDir.exists()) { uploadDir.mkdirs(); } String filePath = uploadPath + File.separator + fileName; try (InputStream inputStream = filePart.getInputStream()) { Files.copy(inputStream, new File(filePath).toPath(), StandardCopyOption.REPLACE_EXISTING); } // 返回成功响应 response.setStatus(HttpServletResponse.SC_OK); response.getWriter().write("上传成功"); } private String getFileName(Part part) { String contentDisp = part.getHeader("content-disposition"); String[] tokens = contentDisp.split(";"); for (String token : tokens) { if (token.trim().startsWith("filename")) { return token.substring(token.indexOf("=") + 2, token.length() - 1); } } return ""; } }
  • // SpringMVC提供简化上传操作的工具类。在不使用框架之前,都是使用原生的HttpServletRequest来接收上传的数据使用了MultipartFile工具类之后,我们对文件上传的操作就简便许多了。
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.util.StringUtils;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.multipart.MultipartFile;
    
    import java.io.File;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    
    @Service
    @RestController
    public class BatchUploadApplication {
    
        @PostMapping("/upload")
        public List<String> uploadFiles(@RequestParam("files") MultipartFile[] files,
                                                                @RequestParam("param1") String param1,
                                                               @RequestParam("param2") String param2) {
            List<String> fileUrls = new ArrayList<>();
    
            // 检查文件数组是否为空
            if (files == null || files.length == 0) {
                throw new IllegalArgumentException("No files to upload");
            }
    
            // 遍历文件数组并保存文件
            for (MultipartFile file : files) {
                if (!file.isEmpty()) {
                    String fileName = StringUtils.cleanPath(file.getOriginalFilename());
                    try {
                        // 保存文件到指定路径
                        String filePath = "/path/to/save/" + fileName;
                        File dest = new File(filePath);
                         // 将MultipartFile类型的文件转化为File类型的文件
                        file.transferTo(dest);
                        fileUrls.add(filePath);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
          
           // 在这里可以对参数进行业务逻辑处理
    
            return fileUrls;
        }
    }
  • //  使用FileUpload: Apache commons下面的一个子项目,用来实现文件上传功能,主要使用 FileItem类、ServletFileUpload 类、DiskFileItemFactory类
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import org.apache.commons.fileupload.FileItem;
    import org.apache.commons.fileupload.FileItemFactory;
    import org.apache.commons.fileupload.disk.DiskFileItemFactory;
    import org.apache.commons.fileupload.servlet.ServletFileUpload;
    
    @WebServlet("/upload")
    public class FileUploadServlet extends HttpServlet {
        private static final long serialVersionUID = 1L;
    
        protected void doPost(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            // 检查请求是否为multipart/form-data类型
            boolean isMultipart = ServletFileUpload.isMultipartContent(request);
            if (!isMultipart) {
                response.sendError(HttpServletResponse.SC_BAD_REQUEST, "请求不是multipart/form-data类型");
                return;
            }
    
            // 创建文件项工厂
            FileItemFactory factory = new DiskFileItemFactory();
    
            // 创建文件上传处理器
            ServletFileUpload upload = new ServletFileUpload(factory);
    
            try {
                // 解析请求,获取文件项列表
                List<FileItem> items = upload.parseRequest(request);
    
                // 遍历文件项列表
                for (FileItem item : items) {
                    // 检查是否为文件项
                    if (!item.isFormField()) {
                        // 获取上传文件的文件名
                        String fileName = item.getName();
    
                        // 创建目标文件
                        String uploadPath = "path/to/upload/directory";
                        File uploadDir = new File(uploadPath);
                        if (!uploadDir.exists()) {
                            uploadDir.mkdirs();
                        }
                        File file = new File(uploadPath + File.separator + fileName);
    
                        // 将文件内容以流的形式写入目标文件
                        try (InputStream inputStream = item.getInputStream();
                             OutputStream outputStream = new FileOutputStream(file)) {
                            byte[] buffer = new byte[4096];
                            int bytesRead;
                            while ((bytesRead = inputStream.read(buffer)) != -1) {
                                outputStream.write(buffer, 0, bytesRead);
                            }
                        }
                    }
                }
    
                // 返回成功响应
                response.setStatus(HttpServletResponse.SC_OK);
                response.getWriter().write("上传成功");
            } catch (Exception e) {
                e.printStackTrace();
                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "上传失败");
            }
        }
    }

     

分片上传

当文件较大时,需要分片上传

//todo

 

前端处理方式

浏览器如果想传输文件,通过表单方式(multipart/form-data)

  • 文件上传为什么要用 multipart/form-data?

    因为application/x-www-form-urlencoded类型不适合用于传输大型二进制数据或者包含非ASCII字符的数据。平常我们使用这个类型都是把表单数据使用url编码后传送给后端,二进制文件当然没办法一起编码进去了。所以multipart/form-data就诞生了,专门用于有效的传输文件。https://zhuanlan.zhihu.com/p/120834588

 

其他资料: