java对MultipartFile操作,文件下载

1、把MultipartFile转File

/**
     * 通过IO流转换(避免临时文件残留)
     *
     * @param multipartFile 上传文件对象
     * @return 转换后的File对象
     * @throws IOException
     */
    public static File convertByStream(MultipartFile multipartFile) throws IOException {
        InputStream inputStream = multipartFile.getInputStream();
        File file = new File(multipartFile.getOriginalFilename());
        try (OutputStream os = new FileOutputStream(file)) {
            byte[] buffer = new byte[8192];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
        }
        inputStream.close();
        return file;
    }

2、把MultipartFile切割成多个小文件

把一个大文件切割成很多个小文件时用到。
下面例子是按照每个文件50KB切割。

/**
     * 切割MultipartFile
     *
     * @param file
     * @param chunkSize
     * @return
     * @throws IOException
     */
    private static final Integer chunkSize = 5 * 1024*10; // 50KB分片
    
    public static List<MultipartFile> splitMultipartFile(MultipartFile file, int chunkSize) throws IOException {
        List<MultipartFile> chunks = new ArrayList<>();
        byte[] buffer = new byte[chunkSize];

        try (InputStream inputStream = file.getInputStream()) {
            int bytesRead;
            int chunkIndex = 0;

            while ((bytesRead = inputStream.read(buffer)) != -1) {
                byte[] chunkData = Arrays.copyOf(buffer, bytesRead);
                MultipartFile chunk = new MockMultipartFile(
                        "chunk_" + chunkIndex,
                        file.getOriginalFilename() + ".part" + chunkIndex,
                        file.getContentType(),
                        chunkData
                );
                chunks.add(chunk);
                chunkIndex++;
            }
        }
        return chunks;
    }

3、MultipartFile切割的小文件保存到磁盘

private static final Integer chunkSize = 5 * 1024 * 10; // 50KB分片
private static final String destDir = "D:\\merge"; // 分片后文件保存目录
    /**
     * 对上传的文件进行切割
     * @param file
     * @throws Exception
     */
    @PostMapping("/fileCutting")
    public void fileCutting(MultipartFile file) throws Exception {
        File sourceFile = convertByStream(file);
        
        try (RandomAccessFile raf = new RandomAccessFile(sourceFile, "r");
             FileChannel channel = raf.getChannel()) {

            long fileSize = sourceFile.length();
            int chunkCount = (int) Math.ceil((double) fileSize / chunkSize);
            ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

            for (int i = 0; i < chunkCount; i++) {
                final int index = i;
                executor.execute(() -> {
                    try {
                        long startPos = (long) index * chunkSize;
                        long endPos = Math.min(startPos + chunkSize, fileSize);
                        String chunkPath = destDir + "/" + sourceFile.getName() + ".part" + index;

                        try (FileOutputStream fos = new FileOutputStream(chunkPath);
                             FileChannel outChannel = fos.getChannel()) {
                            channel.transferTo(startPos, endPos - startPos, outChannel);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                });
            }
            executor.shutdown();
            executor.awaitTermination(1, TimeUnit.HOURS);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

4、切割后的文件进行合并

    private static final String destDir = "D:\\merge"; // 分片后文件保存目录

    @PostMapping("/fileMerger")
    public void fileMerger() throws Exception {
        File dir = new File(destDir);
        List<File> chunkFiles = Arrays.asList(dir.listFiles());
        //指定生成的文件路径
        File outputFile = new File("D:\\merge\\xxx.pdf");
        /**
         * 按文件名自然排序
         * 这个地方一定要按照切割文件的顺序
         */
        chunkFiles.sort(Comparator.comparing(File::getName));
        ExecutorService executor = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
        try (FileOutputStream fos = new FileOutputStream(outputFile);
             BufferedOutputStream bos = new BufferedOutputStream(fos)) {

            List<Future<Integer>> futures = new ArrayList<>();
            for (File chunk : chunkFiles) {
                futures.add(executor.submit(new MergeTask(chunk, bos)));
            }

            // 等待所有任务完成
            for (Future<Integer> future : futures) {
                future.get();
            }
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        } finally {
            executor.shutdown();
        }
    }

    private static class MergeTask implements Callable<Integer> {
        private final File chunkFile;
        private final BufferedOutputStream outputStream;

        MergeTask(File chunkFile, BufferedOutputStream outputStream) {
            this.chunkFile = chunkFile;
            this.outputStream = outputStream;
        }

        @Override
        public Integer call() throws Exception {
            byte[] buffer = new byte[BUFFER_SIZE];
            int bytesRead;
            try (FileInputStream fis = new FileInputStream(chunkFile);
                 BufferedInputStream bis = new BufferedInputStream(fis)) {

                while ((bytesRead = bis.read(buffer)) != -1) {
                    synchronized (outputStream) {
                        outputStream.write(buffer, 0, bytesRead);
                    }
                }
            }
            return (int) chunkFile.length();
        }
    }

5、SpringBoot实现文件下载

/**
     * @param response
     * @功能描述 下载文件:将输入流中的数据循环写入到响应输出流中,而不是一次性读取到内存
     */
    @RequestMapping("/downloadLocal")
    public void downloadLocal(HttpServletResponse response) throws IOException {
        //要下载的文件
        File file = new File("D:\\xxx.docx");
        // 读到流中
        InputStream inputStream = new FileInputStream(file);// 文件的存放路径
        response.reset();
        response.setContentType("application/octet-stream");
        String filename = file.getName();
        //Content-Disposition的作用:告知浏览器以何种方式显示响应返回的文件,用浏览器打开还是以附件的形式下载到本地保存
        //attachment 表示以附件方式下载   inline 表示在线打开
        //filename表示文件的默认名称,因为网络传输只支持URL编码的相关支付,因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称
        response.addHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(filename, "UTF-8"));
        // 告知浏览器文件的大小
        response.addHeader("Content-Length", "" + file.length());
        ServletOutputStream outputStream = response.getOutputStream();
        byte[] b = new byte[1024];
        int len;
        //从输入流中读取一定数量的字节,并将其存储在缓冲区字节数组中,读到末尾返回-1
        while ((len = inputStream.read(b)) > 0) {
            outputStream.write(b, 0, len);
        }
        inputStream.close();
        outputStream.close();
    }
Java 中逐行读取和解析 `MultipartFile` 文件,通常是在 Spring 框架中处理上传文件时的需求。`MultipartFile` 是 Spring 提供的一个接口,用于封装上传的文件内容,尤其适用于 Web 应用程序中通过 HTTP 请求上传的文件。以下是一种常见且高效的逐行读取方式: ### 逐行读取 `MultipartFile` 文件内容 可以通过 `InputStreamReader` 和 `BufferedReader` 来逐行读取文件内容,同时可以使用 `StandardCharsets` 指定字符编码以避免乱码问题。示例代码如下: ```java import org.springframework.web.multipart.MultipartFile; import java.io.BufferedReader; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; public class FileLineReader { public static String readLines(MultipartFile multipartFile) throws Exception { BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(multipartFile.getInputStream(), StandardCharsets.UTF_8) ); StringBuilder stringBuilder = new StringBuilder(); String line; while ((line = bufferedReader.readLine()) != null) { // TODO: 按照业务需求处理每一行内容 stringBuilder.append(line).append("\n"); } return stringBuilder.toString(); } } ``` 上述代码中,`MultipartFile.getInputStream()` 方法用于获取文件的输入流,然后通过 `InputStreamReader` 按照指定的字符编码(如 UTF-8)将字节流转换为字符流。`BufferedReader` 则提供了高效的逐行读取能力。 ### 处理大文件的注意事项 如果文件体积较大,建议不要一次性将整个文件内容加载到内存中,而是逐行处理并释放资源。此外,可以结合 `try-with-resources` 语句确保流在使用完毕后自动关闭,从而避免资源泄漏问题: ```java try (BufferedReader reader = new BufferedReader( new InputStreamReader(multipartFile.getInputStream(), StandardCharsets.UTF_8))) { String line; while ((line = reader.readLine()) != null) { // 处理每一行数据 } } ``` ### 使用 Java 8 Stream API 进行更灵活的处理 如果希望使用 Java 8 的 Stream API 来处理每一行内容,可以借助 `BufferedReader.lines()` 方法,该方法返回一个 `Stream<String>`,允许使用函数式编程风格对每一行进行操作: ```java import java.util.stream.Collectors; String content = new BufferedReader( new InputStreamReader(multipartFile.getInputStream(), StandardCharsets.UTF_8) ).lines() .map(line -> processLine(line)) // 自定义处理逻辑 .collect(Collectors.joining("\n")); ``` 其中,`processLine()` 可以是任意对每一行字符串进行处理的方法。 ### 总结 通过 `BufferedReader` 配合 `InputStreamReader`,可以高效地逐行读取 `MultipartFile` 文件内容,并根据实际需求进行解析和处理。对于大文件,应避免一次性加载全部内容到内存中,而应采用流式处理的方式逐行读取和处理[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北漂IT民工_程序员_ZG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值