本文所有内容都是流式上传(数据通过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
其他资料:
-
解压缩:zip4j类。https://www.coder.work/article/6454309
-
上传文件接口参考:上传文件
-
批量上传文件:MultipartFile[] file https://www.jianshu.com/p/74802e8385cf
-
MultipartFile 和 CommonsMultipartFile的区别 https://www.cnblogs.com/jia0504/p/13991578.html