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);
}
};
}
}