使用SpringBoot的虚拟路径映射。
Config中的类
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.WebMvcConfigurerAdapter;
/**
* 图片绝对地址与虚拟地址映射
*/
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Value("${realityFileDepositPath}")
private String realityFileDepositPath;
@Value("${virtualFileDepositPath}")
private String virtualFileDepositPath;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(virtualFileDepositPath)
.addResourceLocations("file:" + realityFileDepositPath);
}
}
Control中的类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import xin.students.service.UpdateOrDownloadService;
@RestController
public class UpdateOrDownloadController {
@Autowired
UpdateOrDownloadService updateOrDownloadService;
@PostMapping("/api/upload")
public ResponseEntity<String> handleFileUpload(@RequestParam("file") MultipartFile file) {
return updateOrDownloadService.handleFileUpload(file);
}
@GetMapping("/api/upload/{fileName:.+}")
public ResponseEntity<Resource> downloadFile(@PathVariable String fileName) {
return updateOrDownloadService.downloadFile(fileName);
}
}
Service中的类
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.http.HttpHeaders;
import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
@Service
public class UpdateOrDownloadService {
@Value("${virtualFileDepositPath}")
private String virtualFileDepositPath;
@Value("${realityFileDepositPath}")
private String realityFileDepositPath;
public ResponseEntity<String> handleFileUpload(@RequestParam("file") MultipartFile file) {
// 获取文件名
String fileName = file.getOriginalFilename();
// 确保文件名存在并且不是.exe文件
if (fileName != null && !fileName.toLowerCase().endsWith(".exe")) {
fileName = System.currentTimeMillis() + fileName;
// 检查文件大小是否合适
if (file.getSize() <= 10 * 1024 * 1024) { // 10 MB
// 保存文件
try {
String fullPath = realityFileDepositPath + fileName;
file.transferTo(new File(fullPath));
System.out.println("Public document has been saved to: " + fullPath);
} catch (IOException e) {
e.printStackTrace();
return new ResponseEntity<>("Public document upload failed due to internal error", HttpStatus.INTERNAL_SERVER_ERROR);
}
// 返回文件访问虚拟路径
return new ResponseEntity<>(virtualFileDepositPath.substring(0, virtualFileDepositPath.length() - 2) + fileName, HttpStatus.OK);
} else {
return new ResponseEntity<>("Public document size exceeds 10MB", HttpStatus.BAD_REQUEST);
}
} else {
// 提供一个不同的错误消息
return new ResponseEntity<>("Public document format is not supported or is executable, which is not allowed", HttpStatus.BAD_REQUEST);
}
}
public ResponseEntity<Resource> downloadFile(String fileName) {
try {
// 确定文件的存储路径
Path filePath = Paths.get(realityFileDepositPath).resolve(fileName).normalize();
Resource resource = new UrlResource(filePath.toUri());
if(resource.exists() || resource.isReadable()) {
// 确保文件名编码正确,避免乱码
String encodedFileName = URLEncoder.encode(resource.getFilename(), StandardCharsets.UTF_8.toString());
String contentDisposition = ContentDisposition.builder("attachment")
.filename(encodedFileName, StandardCharsets.UTF_8)
.build()
.toString();
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, contentDisposition)
.body(resource);
} else {
return ResponseEntity.notFound().build();
}
} catch (IOException e) {
// 如果无法读取文件,返回错误响应
return ResponseEntity.internalServerError().build();
}
}
}
application.yml配置文件
virtualFileDepositPath: /api/image/**
realityFileDepositPath: D:\xinFiles\ #结束必须要带/
测试代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>文件上传和下载</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
flex-direction: column;
}
.container {
text-align: center;
}
input[type="text"], input[type="file"] {
margin: 10px 0;
}
</style>
<script>
function uploadFile() {
var formData = new FormData();
var fileField = document.querySelector("input[type=file]");
formData.append('file', fileField.files[0]);
fetch('/api/upload', {
method: 'POST',
body: formData
})
.then(response => response.text())
.then(result => {
alert('上传成功: ' + result);
})
.catch(error => {
console.error('上传出错:', error);
alert('上传失败');
});
}
function downloadFile() {
var fileName = document.getElementById("filename").value;
if (fileName) {
window.open('/api/upload/' + fileName, '_blank');
} else {
alert('请输入文件名');
}
}
</script>
</head>
<body>
<div class="container">
<h1>文件上传</h1>
<input type="file" id="fileInput"><br>
<button onclick="uploadFile()">上传文件</button>
<h1>文件下载</h1>
<input type="text" id="filename" placeholder="请输入文件名"><br>
<button onclick="downloadFile()">下载文件</button>
</div>
</body>
</html>