二、Web服务常用的I/O操作

一、单个或者批量上传文件

前端:

html 复制代码
<!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>

后端:

java 复制代码
 @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要配置下文件上传大小限制:

properties 复制代码
# 设置单个文件的最大上传大小 (默认是1M)
spring.servlet.multipart.max-file-size=50MB
# 设置请求中所有文件的最大上传大小  (默认是10M)
spring.servlet.multipart.max-request-size=1000MB

二、后端上传文件

小文件处理,全部加载到内存

java 复制代码
@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) {
                @Override
                public 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流式处理

xml 复制代码
<dependency>
   <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
java 复制代码
 @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) {
                @Override
                public String getFilename() {
                    return filePath.getFileName().toString();
                }

                @Override
                public 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 下载

java 复制代码
@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, "文件未找到!");
        }
    }

四、后端下载文件

java 复制代码
@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();
        }
    }
相关推荐
缺点内向1 天前
Java:创建、读取或更新 Excel 文档
java·excel
带刺的坐椅1 天前
Solon v3.4.7, v3.5.6, v3.6.1 发布(国产优秀应用开发框架)
java·spring·solon
四谎真好看1 天前
Java 黑马程序员学习笔记(进阶篇18)
java·笔记·学习·学习笔记
桦说编程1 天前
深入解析CompletableFuture源码实现(2)———双源输入
java·后端·源码
java_t_t1 天前
ZIP工具类
java·zip
lang201509281 天前
Spring Boot优雅关闭全解析
java·spring boot·后端
pengzhuofan1 天前
第10章 Maven
java·maven
百锦再1 天前
Vue Scoped样式混淆问题详解与解决方案
java·前端·javascript·数据库·vue.js·学习·.net
刘一说1 天前
Spring Boot 启动慢?启动过程深度解析与优化策略
java·spring boot·后端
壹佰大多1 天前
【spring如何扫描一个路径下被注解修饰的类】
java·后端·spring