二、Web服务常用的I/O操作
一、单个或者批量上传文件
前端:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>文件上传示例</title><style>body {font-family: Arial, sans-serif;margin: 20px;line-height: 1.6;}button {padding: 10px 15px;background-color: #4CAF50;color: white;border: none;cursor: pointer;font-size: 16px;}button:hover {background-color: #45a049;}#result {margin-top: 20px;padding: 10px;border: 1px solid #ddd;background-color: #f9f9f9;}</style>
</head>
<body><h1>文件上传</h1><h2>单个文件上传</h2><form id="singleUploadForm"><input type="file" id="singleFileInput" name="file" required><button type="button" onclick="uploadSingleFile()">上传文件</button></form><h2>批量文件上传</h2><form id="batchUploadForm"><input type="file" id="batchFileInput" name="files" multiple required><button type="button" onclick="uploadBatchFiles()">上传文件</button></form><div id="result"></div><script>function uploadSingleFile() {const fileInput = document.getElementById('singleFileInput');const file = fileInput.files[0];if (!file) {alert('请选择一个文件');return;}const formData = new FormData();formData.append('file', file);fetch('http://localhost:8888/test/uploadFile', {method: 'POST',body: formData}).then(response => response.text()).then(data => {document.getElementById('result').innerHTML = data;}).catch(error => {document.getElementById('result').innerHTML = '上传失败: ' + error.message;});}function uploadBatchFiles() {const fileInput = document.getElementById('batchFileInput');const files = fileInput.files;if (!files || files.length === 0) {alert('请选择至少一个文件');return;}const formData = new FormData();for (let i = 0; i < files.length; i++) {formData.append('files', files[i]);}fetch('http://localhost:8888/test/uploadFiles', {method: 'POST',body: formData}).then(response => response.text()).then(data => {document.getElementById('result').innerHTML = data;}).catch(error => {document.getElementById('result').innerHTML = '上传失败: ' + error.message;});}</script>
</body>
</html>
后端:
@PostMapping("test/uploadFile")public String upload(@RequestParam("file") MultipartFile file) {try {// 指定保存路径String savePath = "C:\\Users\\test\\Desktop\\";// 获取原始文件名String originalFilename = file.getOriginalFilename();if (originalFilename == null || originalFilename.isEmpty()) {return "文件名不能为空!";}// 构建完整路径Path targetPath = Paths.get(savePath + originalFilename);// 确保目录存在Files.createDirectories(targetPath.getParent());// 保存文件(替换已存在文件)Files.copy(file.getInputStream(), targetPath, StandardCopyOption.REPLACE_EXISTING);return "文件上传成功!保存路径: " + targetPath.toString();} catch (IOException e) {e.printStackTrace();return "文件上传失败: " + e.getMessage();}}@PostMapping("/test/uploadFiles")public String uploadFiles(@RequestParam("files") MultipartFile[] files) {try {String savePath = "C:\\Users\\test\\Desktop\\";List<String> uploadedFiles = Arrays.stream(files).map(file -> {try {String originalFilename = file.getOriginalFilename();if (originalFilename == null || originalFilename.isEmpty()) {return "文件名不能为空!";}Path targetPath = Paths.get(savePath + originalFilename);Files.createDirectories(targetPath.getParent());Files.copy(file.getInputStream(), targetPath, StandardCopyOption.REPLACE_EXISTING);return "文件上传成功!保存路径: " + targetPath.toString();} catch (IOException e) {return "文件上传失败: " + e.getMessage();}}).collect(Collectors.toList());return String.join("<br>", uploadedFiles);} catch (Exception e) {e.printStackTrace();return "文件上传失败: " + e.getMessage();}}
springboot要配置下文件上传大小限制:
# 设置单个文件的最大上传大小 (默认是1M)
spring.servlet.multipart.max-file-size=50MB
# 设置请求中所有文件的最大上传大小 (默认是10M)
spring.servlet.multipart.max-request-size=1000MB
二、后端上传文件
小文件处理,全部加载到内存
@GetMapping("/test/backUpload")public void backUpload() {String uploadUrl = "http://localhost:8888/test/uploadFile";String sourcePath = "C:\\Users\\test\\Desktop\\ttt\\666.txt";Path filePath = Paths.get(sourcePath);if (!Files.exists(filePath)) {System.out.println("文件不存在!");return;}try {byte[] fileBytes = Files.readAllBytes(filePath); // NIO读取文件内容到byte数组ByteArrayResource resource = new ByteArrayResource(fileBytes) {@Overridepublic String getFilename() {return filePath.getFileName().toString();}};HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.MULTIPART_FORM_DATA);MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();body.add("file", resource);HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);ResponseEntity<String> response = new RestTemplate().postForEntity(uploadUrl, requestEntity, String.class);System.out.println("上传结果:" + response.getBody());} catch (IOException e) {e.printStackTrace();}}
大文件处理使用webflux流式处理
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
@GetMapping("/test/backUploadNew")public void backUploadNew() {String uploadUrl = "http://localhost:8888/test/uploadFile";String sourcePath = "C:\\Users\\test\\Desktop\\ttt\\666.txt";Path filePath = Paths.get(sourcePath);if (!Files.exists(filePath)) {System.out.println("文件不存在!");return;}try {InputStream inputStream = Files.newInputStream(filePath);InputStreamResource resource = new InputStreamResource(inputStream) {@Overridepublic String getFilename() {return filePath.getFileName().toString();}@Overridepublic long contentLength() throws IOException {return Files.size(filePath);}};MultipartBodyBuilder builder = new MultipartBodyBuilder();builder.part("file", resource).header("Content-Disposition", "form-data; name=file; filename=" + filePath.getFileName());HttpClient httpClient = HttpClient.create();ClientHttpConnector connector = new ReactorClientHttpConnector(httpClient);WebClient webClient = WebClient.builder().clientConnector(connector).build();webClient.method(HttpMethod.POST).uri(uploadUrl).contentType(MediaType.MULTIPART_FORM_DATA).bodyValue(builder.build()).retrieve().bodyToMono(String.class).doOnNext(result -> System.out.println("上传结果:" + result)).block(); // 等待上传完成} catch (IOException e) {e.printStackTrace();}}
三、前端下载文件
前端直接访问接口 /test/download 下载
@GetMapping("/test/download")public void download(HttpServletResponse response) throws IOException {// 文件路径Path filePath = Paths.get("C:\\Users\\test\\Desktop\\哈哈+ +c.txt"); // 替换成你的实际路径if (Files.exists(filePath)) {response.setContentType("application/octet-stream");String fileName = filePath.getFileName().toString();// 处理中文文件名:防止下载时乱码String encodedFileName = java.net.URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");response.setHeader("Content-Disposition","attachment; filename=\"" + new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1) + "\"; filename*=UTF-8''" + encodedFileName);// 直接用 Files.copy,拷贝文件到 response 的输出流Files.copy(filePath, response.getOutputStream());response.flushBuffer(); // 刷新缓冲区,确保数据发出去} else {response.sendError(HttpServletResponse.SC_NOT_FOUND, "文件未找到!");}}
四、后端下载文件
@GetMapping("/test/backDownload")public void backDownload() {String url = "http://localhost:8888/test/download";String destinationPath = "C:\\Users\\test\\Desktop\\ttt\\666.txt";try {ResponseEntity<Resource> response = new RestTemplate().getForEntity(url, Resource.class);if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {Path path = Paths.get(destinationPath);Files.createDirectories(path.getParent()); // 确保父目录存在Files.copy(response.getBody().getInputStream(), path);System.out.println("下载完成!");} else {System.out.println("下载失败,状态码:" + response.getStatusCode());}} catch (IOException e) {e.printStackTrace();}}