二、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();
        }
    }
相关推荐
北执南念11 分钟前
项目代码生成工具
java
中国lanwp17 分钟前
springboot logback 默认加载配置文件顺序
java·spring boot·logback
苹果酱05671 小时前
【Azure Redis 缓存】在Azure Redis中,如何限制只允许Azure App Service访问?
java·vue.js·spring boot·mysql·课程设计
Java致死1 小时前
单例设计模式
java·单例模式·设计模式
胡子发芽1 小时前
请详细解释Java中的线程池(ThreadPoolExecutor)的工作原理,并说明如何自定义线程池的拒绝策略
java
沫夕残雪1 小时前
Tomcat的安装与配置
java·tomcat
胡子发芽2 小时前
请解释Java中的NIO(New I/O)与传统I/O的区别,并说明NIO中的关键组件及其作用
java
柚个朵朵2 小时前
IDEA中使用Git
java·git·spring
jerry6092 小时前
优先队列、堆笔记(算法第四版)
java·笔记·算法
666HZ6662 小时前
关于IDEA的循环依赖问题
java·ide·intellij-idea