文件下载上传预览以及上传端进度条

List<MultipartFile> files = ((MultipartHttpServletRequest) request).getFiles("file");
for (int i = 0; i < files.size(); ++i) {
        file = files.get(i);
        if (!file.isEmpty()) {
        }
}

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
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 org.springframework.web.servlet.mvc.support.RedirectAttributes;
@RestController // 如果是API接口,使用@RestController;如果是处理视图,则使用@Controller
public class FileUploadController {
    @Autowired
    private FileStorageService fileStorageService; // 假设有一个用于处理文件存储的服务

    @PostMapping("/upload-multiple")
    public ResponseEntity<String> handleFileUpload(@RequestParam("files") MultipartFile[] files, RedirectAttributes redirectAttributes) {
        List<String> savedFilesPaths = new ArrayList<>();

        for (MultipartFile file : files) {
            if (!file.isEmpty()) {
                try {
                    String storedFilePath = fileStorageService.store(file); // 调用服务类的方法来存储文件并返回路径
                    savedFilesPaths.add(storedFilePath);
                } catch (IOException e) {
                    // 处理文件上传失败的情况
                    e.printStackTrace();
                    return new ResponseEntity<>("File upload failed: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);

                }
            } else {
               return new ResponseEntity<>("File upload empty" , HttpStatus.INTERNAL_SERVER_ERROR);
            }
        }
        // 将已成功上传并存储的文件路径添加到集合中,这里直接返回或用于后续业务逻辑
        return new ResponseEntity<>("File uploaded successfully: " + "1.jpg", HttpStatus.OK);
    }
}


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
@Service
public class FileStorageService {

    private final Path fileStorageLocation; // 文件存储位置,需要在配置类中注入
    @Autowired
    public FileStorageService(FileStorageProperties properties) {
        this.fileStorageLocation = Paths.get(properties.getLocation());
    }
    public String store(MultipartFile file) throws IOException {
        // 创建文件名(可以自定义生成策略)
        Path targetLocation = this.fileStorageLocation.resolve(file.getOriginalFilename());
        // 保存文件
        Files.copy(file.getInputStream(), targetLocation, StandardCopyOption.REPLACE_EXISTING);
        // 返回存储路径
        return targetLocation.toString();
    }
}

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class ResourcesConfig implements WebMvcConfigurer{  
	
   @Value("${filestorage.location}")
   private String fileURl;
   @Override
   public void addResourceHandlers(ResourceHandlerRegistry registry)
   {
       /** 通过url访问项目外的目录图片*/
       registry.addResourceHandler("/cert/**").addResourceLocations("file:/D:/load/img/");
   }
}

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class StorageConfiguration {
    @Bean
    public FileStorageProperties fileStorageProperties() {
        return new FileStorageProperties();
    }
}

@ConfigurationProperties(prefix = "filestorage")
class FileStorageProperties {
    private String location = "upload-dir";
	public String getLocation() {
		return location;
	}
	public void setLocation(String location) {
		this.location = location;
	}
}
application.properties
filestorage.location=/path/to/your/upload/folder

单文件上传

@PostMapping("/upload")
public ResponseEntity<String> handleFileUpload(@RequestParam("file") MultipartFile file) {
     if (file.isEmpty()) {
         return ResponseEntity.badRequest().body("Please select a file to upload.");
     }
     try {
         // 保存文件到服务器指定目录
         Path filePath = Paths.get("/" + file.getOriginalFilename());
         Files.copy(file.getInputStream(), filePath);//FileUtils.copyInputStreamToFile(source, destination);
         //或者file.transferTo(dest);
         return ResponseEntity.ok()
                 .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getOriginalFilename() + "\"")
                 .body("Successfully uploaded - " + file.getOriginalFilename());
     } catch (IOException e) {
         return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                 .body("Failed to store file: " + e.getMessage());
     }
 }

大文件分块下载

package com.rui;
import java.io.*;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.List;
public class FileChunkingAndMergingTest {

    private static final int CHUNK_SIZE = 1024 * 1024; // 假设每个块大小为1MB

    public static void main(String[] args) throws IOException {
        String sourceFilePath = "D:\\apache-tomcat-8.5.75.zip"; // 大文件路径
        String tempDirectoryPath = "C:\\abc"; // 临时目录用于存放分块
        String targetFilePath = "C:\\abc\\merged-tomcat.zip"; // 合并后的文件路径
        // 分割文件到临时目录
        List<String> chunkFiles = splitFile(sourceFilePath, tempDirectoryPath, CHUNK_SIZE);
        // 合并分块文件到目标文件
        mergeChunks(chunkFiles, targetFilePath);
        System.out.println("File has been successfully split and merged.");
    }

    /**
     * 将大文件分割成多个小文件,并将各个分块文件路径存入列表返回。
     */
    public static List<String> splitFile(String sourceFilePath, String tempDirectoryPath, int chunkSize) throws IOException {
        File sourceFile = new File(sourceFilePath);
        File tempDir = new File(tempDirectoryPath);
        if (!tempDir.exists() && !tempDir.mkdirs()) {
            throw new IOException("Failed to create temporary directory: " + tempDirectoryPath);
        }

        List<String> chunkFilePaths = new ArrayList<>();
        try (FileInputStream fis = new FileInputStream(sourceFile);
             FileChannel fileChannel = fis.getChannel()) {

            long fileSize = fileChannel.size();
            for (long position = 0; position < fileSize; position += chunkSize) {
                long remainingFileSize = fileSize - position;
                long chunkEnd = Math.min(position + chunkSize - 1, fileSize - 1);

                String chunkFileName = "chunk-" + position + ".part";
                File chunkFile = new File(tempDirectoryPath, chunkFileName);
                try (FileOutputStream fos = new FileOutputStream(chunkFile);
                     FileChannel outChannel = fos.getChannel()) {

                    fileChannel.transferTo(position, Math.min(chunkSize, remainingFileSize), outChannel);
                }

                chunkFilePaths.add(chunkFile.getAbsolutePath());
            }
        }

        return chunkFilePaths;
    }

    /**
     * 将多个分块文件按顺序合并到目标文件。
     */
    public static void mergeChunks(List<String> chunkFilePaths, String targetFilePath) throws IOException {
        File targetFile = new File(targetFilePath);
        try (FileOutputStream fos = new FileOutputStream(targetFile)) {
            FileChannel outChannel = fos.getChannel();

            for (String chunkFilePath : chunkFilePaths) {
                File chunkFile = new File(chunkFilePath);
                try (FileInputStream fis = new FileInputStream(chunkFile);
                     FileChannel inChannel = fis.getChannel()) {

                    inChannel.transferTo(0, inChannel.size(), outChannel);
                }finally {
                    // 在每次成功转移数据后删除分块文件
                    if (!chunkFile.delete()) {
                        System.err.println("Failed to delete the chunk file: " + chunkFilePath);
                    }
                }

            }
        }
        
        // 确保所有分块都已成功合并并删除,这里可以进行额外的清理操作
//        for (String chunkFilePath : chunkFilePaths) {
//            File chunkFile = new File(chunkFilePath);
//            if (chunkFile.exists() && !chunkFile.delete()) { // 如果由于某种原因之前未删除,再次尝试删除
//            	FileUtils.deleteQuietly(chunkFile);
//                System.err.println("Final attempt to delete the chunk file failed: " + chunkFilePath);
//            }
//        }

    }
}

异步下载

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
    // 配置自定义线程池(如有需要)
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2); // 根据实际情况调整核心线程数
        executor.setMaxPoolSize(10); // 根据实际情况调整最大线程数
        executor.initialize();
        return executor;
    }
}
ThreadPoolExecutor executor = new ThreadPoolExecutor(20, 100, 10000, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10000));

@Autowired
private AsyncConfig asyncConfig; // 注入配置类以使用异步线程池


@GetMapping("/{filename}")
public CompletableFuture<ResponseEntity<StreamingResponseBody>> downloadFiles(@PathVariable String filename) {
        return CompletableFuture.supplyAsync(() -> {
            Path filePath = Paths.get("C:\\Users\\8888\\Desktop\\", filename);
            if (!Files.exists(filePath)) {
            } 
            String size = null;
			try {
				size = String.valueOf(Files.size(filePath));
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
            return ResponseEntity.ok()
                    .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\"")
                    .header(HttpHeaders.CONTENT_LENGTH,size)
                    .contentType(MediaType.APPLICATION_OCTET_STREAM)
                    .body(out -> {
                        try (InputStream inputStream = Files.newInputStream(filePath)) {
                            byte[] buffer = new byte[4096];
                            int bytesRead;
                            while ((bytesRead = inputStream.read(buffer)) != -1) {
                                out.write(buffer, 0, bytesRead);
                            }
                        } catch (IOException e) {
                        }
            });
        }, executor);//asyncConfig.getAsyncExecutor()
}

普通下载

@GetMapping("/download/stream/{filename}")
public ResponseEntity<StreamingResponseBody> streamDownload(@PathVariable String filename) throws IOException {
        File file = new File("C:\\abc\\merged-tomcat.zip");
        if (!file.exists()) {
            return ResponseEntity.notFound().build();
        }

        HttpHeaders headers = new HttpHeaders();
        headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\"");
        StreamingResponseBody streamingBody = outputStream -> {
            try (InputStream inputStream = new FileInputStream(file)) {
                byte[] buffer = new byte[4096]; // 假设每次读取4KB
                int n;
                while ((n = inputStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, n);
                }
            } catch (IOException ex) {
                throw new RuntimeException("IO Error writing to output stream", ex);
            }
        };
	    ResponseEntity<StreamingResponseBody> responseEntity = ResponseEntity.ok().headers(headers).contentLength(file.length()).contentType(MediaType.parseMediaType("application/octet-stream")).body(streamingBody);
        return responseEntity;
}

预览

private static final Map<String, MediaType> MIME_TYPES = new HashMap<String, MediaType>(){{
       put("pdf", MediaType.APPLICATION_PDF);
       put("doc", MediaType.APPLICATION_OCTET_STREAM);
       put("xls", MediaType.APPLICATION_OCTET_STREAM);
       put("jpg", MediaType.IMAGE_PNG);
       put("png", MediaType.IMAGE_PNG);
       put("gif", MediaType.IMAGE_GIF);            
   }};
   @SuppressWarnings("unused")
   @GetMapping("common/download")
   public ResponseEntity<Object> fileDownload(HttpServletResponse response) throws Exception
   {
   	ClassPathResource resource = new ClassPathResource("1.pdf");
   	//resource = new ClassPathResource("1.jpg");
       String extension = FilenameUtils.getExtension(resource.getFilename()).toLowerCase();
       MediaType mediaType = MIME_TYPES.getOrDefault(extension, MediaType.APPLICATION_OCTET_STREAM);
       
       HttpHeaders headers = new HttpHeaders();
       headers.setContentType(mediaType);
       if(org.apache.commons.lang3.StringUtils.equalsAnyIgnoreCase(extension, "jpg","png","gif")) {
       	 headers.set(HttpHeaders.CONTENT_DISPOSITION, "fileName="+resource.getFilename());
       	 //headers.setContentType(MediaType.parseMediaType(MediaType.IMAGE_PNG_VALUE));
       	 //headers.setContentType(HttpHeaders.CONTENT_TYPE,MediaType.IMAGE_PNG_VALUE);
       	 headers.setContentType(mediaType); 
       	 //预览
       	 if(1==1) {
           	 headers.set(HttpHeaders.CONTENT_LENGTH, ""+resource.getFile().length()+"");
           	 headers.set(HttpHeaders.CONNECTION, "close");
       	 }else {
       	     //下载
             headers.setContentDispositionFormData("attachment", new String(resource.getFilename().getBytes("UTF-8"), "ISO-8859-1"));

       	 }
       }else {
       	if(!mediaType.equals(MediaType.APPLICATION_PDF)) {
           	//用于下载
           	headers.setContentDispositionFormData("attachment", resource.getFilename());
           }else {
           	//预览
        	//修改title
        	//加载 PDF 文档
            PDDocument document = PDDocument.load(resource.getInputStream());
            // 设置文档信息
            PDDocumentInformation info = document.getDocumentInformation();
            info.setTitle("自定义标题");
            info.setAuthor("作者");
            info.setSubject("主题");
            info.setKeywords("关键词");
            // 将 PDF 文档保存到字节数组输出流中
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            document.save(outputStream);
            // 关闭文档
            document.close();
            // 将字节数组输出流转换为输入流
            InputStream previewInputStream = new ByteArrayInputStream(outputStream.toByteArray());
            // 设置响应头
            //headers.setContentType(MediaType.APPLICATION_PDF);可有可无
            return ResponseEntity.ok()
                    .headers(headers)
                    .body(new InputStreamResource(previewInputStream));
           	//headers.set("Content-Disposition", "inline; filename*=UTF-8''" + URLEncoder.encode(resource.getFilename(), "UTF-8")); 
            //或者但是setTitle("自定义标题");改不了
           	//IOUtils.copy(resource.getInputStream(), response.getOutputStream());
           	//response.setContentType("application/pdf;chartset=UTF-8");
           }
       }
       return ResponseEntity.ok()
               .headers(headers)
       		//.header(HttpHeaders.CONTENT_DISPOSITION, "inline;fileName=\"2.pdf\"")
       		//.header(HttpHeaders.CONTENT_TYPE, "application/pdf")
               .body(resource); 
       
       
//       if(file.isPresent()){
//           return ResponseEntity.ok()
//                   .header(HttpHeaders.CONTENT_DISPOSITION, "fileName=\""+file.get().getName()+"\"")
//                   .header(HttpHeaders.CONTENT_TYPE, file.get().getContentType())
//                   .header(HttpHeaders.CONTENT_LENGTH, file.get().getSize()+"")
//                   .header(HttpHeaders.CONNECTION, "close")
//                   .body(file.get().getContent().getData());
//       }else{
//           return ResponseEntity.status(HttpStatus.NOT_FOUND).body("file was not found");
//       }

   }
 
 <dependency>
		  <groupId>org.apache.pdfbox</groupId>
		  <artifactId>pdfbox</artifactId>
		  <version>2.0.24</version>
 </dependency>

web端进度条

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>Image Upload with Progress</title>
   <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>
<style>
#progress-bar {
    width: 100%;
    background-color: #f3f3f3;
    position: relative;
}

#progress-bar {
    height: 30px;
    background-color: #4CAF50;
    width: 0%;
    position: absolute;
    text-align: center;
    line-height: 30px;
    color: white;
}
</style>
<body>
    <h1>Image Upload with Progress</h1>
    <form id="upload-form"  enctype="multipart/form-data">
       <input type="file" name="file" id="file-input" >
       <button type="submit">Upload</button>
    </form>
    <div id="progress-bar-container">
        <div id="progress-bar"></div>
    </div>
   <script>
	$(document).ready(function () {
    $("#upload-form").on("submit", function (e) {
        e.preventDefault();

        var fileInput = $("#file-input")[0];
        if (fileInput.files.length === 0) {
             alert("请选择一个文件");
             return;
        }
        var file = fileInput.files[0];
        var formData = new FormData();
        formData.append("file", file);

        $.ajax({
            url: "http://localhost:8072/sendMessage",
            type: "post",
            data: formData,
            processData: false,
            contentType: false,
            xhr: function () {
                var xhr = new window.XMLHttpRequest();
                xhr.upload.addEventListener("progress", function (evt) {
                    if (evt.lengthComputable) {
                        var percentComplete = evt.loaded / evt.total;
                        percentComplete = parseInt(percentComplete * 100);
                        $("#progress-bar").css("width", percentComplete + "%");
                    }
                }, false);
                return xhr;
            },
            success: function (response) {
				// 更新进度条宽度为100%
				$("#progress-bar").css("width", "100%");
                alert("File uploaded successfully: " + response);
            },
            error: function (error) {
                alert("Error uploading file: " + error);
            }
        });
    });
});
	</script>
</body>
</html>


spring:
  # 文件上传
  servlet:
     multipart:
       # 单个文件大小
       max-file-size:  100MB
       # 设置总上传的文件大小
       max-request-size:  200MB
 
   @PostMapping("/sendMessage")
   public ResponseEntity<String> handleFileUpload(@RequestParam("file") MultipartFile file) {
       if (file.isEmpty()) {
           return new ResponseEntity<>("File is empty", HttpStatus.BAD_REQUEST);
       }

       try {
           // Save the file to a specific location
           File dest = new File("c://" + file.getOriginalFilename());
           file.transferTo(dest);
           return new ResponseEntity<>("File uploaded successfully: " + dest.getAbsolutePath(), HttpStatus.OK);
       } catch (IOException e) {
           e.printStackTrace();
           return new ResponseEntity<>("Error uploading file: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
       }
   }


@Configuration
public class CorsConfig {
   
    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**")
                        .allowedOrigins("*")
                        .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                        .allowedHeaders("*")
                        .allowCredentials(true)
                        .maxAge(3600);
            }
        };
    }
}
  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

有知识的山巅

文章对你有用,学到了知识。

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

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

打赏作者

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

抵扣说明:

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

余额充值