java 上传文件和下载/预览文件 包括音频调进度条
·
话不多说,直上代码
包括knife4j 3.0文档展示使用
一:Controller
// 接口排序
@ApiOperationSupport(order = 1)
// 接口名称
@Operation(summary = "上传文件")
// 接口返回示例
@ApiResponses({
@ApiResponse(
responseCode = "200",
description = "成功",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = TempFile .class)
)
)
})
@PostMapping("/upload")
public AjaxResult upload(@Parameter(name = "files", description = "文件s", required = true, schema = @Schema(type = "array", implementation = MultipartFile.class))
@RequestPart("files") MultipartFile[] files,
@Parameter(name = "meetingId", description = "会议ID")
@RequestParam("meetingId") Long meetingId, {
List<TempFile> list = new ArrayList<>();
for (MultipartFile file : files) {
try {
// 防止文件名中文乱码
String dbName = URLDecoder.decode(file.getOriginalFilename(), "UTF-8");
// 文件名前面加了时间戳,防止重复上传替换了
String name = DateUtils.getTimeNew() + "_" + dbName;
// 保存路径 global是yml拿的;
String path = global.getFilePath() + "/" + meetingId;
// 进行上传
FileUtils.upload(path, name, file);
// 获取文件后缀名(格式)
String fileFormat = "";
if (dbName != null && dbName.contains(".")) {
fileFormat = dbName.substring(dbName.lastIndexOf(".") + 1).toLowerCase();
}
// 获取文件大小(转换为MB,保留两位小数)
String fileSize = FileUtils.formatFileSize(file.getSize());
// 根据文件后缀判断文件类型
String fileType = FileUtils.getFileType(fileFormat);
// 保存数据库
TempFile tempFile = TempFile.builder()
.fileName(dbName)
.fileFormat(fileFormat)
.fileType(fileType)
.fileSize(fileSize)
.filePath(path + "/" + name)
.build();
mapper.addTempFile(tempFile);
} catch (Exception e) {
e.printStackTrace();
} finally {
list.add(tempFile);
}
return AjaxResult.success(list);
}
}
// 默认是下载,inline是在线预览,支持音频调整进度条
@ApiOperationSupport(order = 2)
@Operation(summary = "下载或预览文件")
@GetMapping("/download")
public void download(@RequestParam(name = "fileId") Long fileId,
@Parameter(name = "disposition", description = "attachment(默认下载)、inline(在线预览)")
@RequestParam(name = "disposition", required = false, defaultValue = "attachment") String disposition,
HttpServletRequest request,
HttpServletResponse response) throws IOException {
TempFile tempFile= meetingFileService.selectTempFile (fileId);
if (null == tempFile) {
throw new RuntimeException("资源不存在!");
}
FileUtils.download(request, response, tempFile.getVoiceName(), tempFile.getVoicePath(), disposition);
}
二:FileUtils
package org.example.common.utils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
/**
* 文件上传工具类
*
* @author lt
*/
@Slf4j
public class FileUtils {
public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.]+";
// 文档类型
public static String[] documentFormats = {"doc", "docx", "pdf", "txt", "xls", "xlsx", "ppt", "pptx", "rtf", "odt"};
// 图片类型
public static String[] imageFormats = {"jpg", "jpeg", "png", "gif", "bmp", "webp", "tiff", "svg"};
// 视频类型
public static String[] videoFormats = {"mp4", "avi", "mov", "wmv", "flv", "mkv", "webm", "mpeg", "3gp"};
/**
* 文件名称验证
*
* @param filename 文件名称
* @return true 正常 false 非法
*/
public static boolean isValidFilename(String filename) {
return filename.matches(FILENAME_PATTERN);
}
/**
* 根据文件路径上传
*
* @param baseDir 相对应用的基目录
* @param file 上传的文件
* @return 文件名称
* @throws IOException
*/
public static void upload(String baseDir, MultipartFile file) throws IOException {
try {
// 获取后缀名称
String fileName = file.getOriginalFilename();
String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath();
file.transferTo(Paths.get(absPath));
} catch (Exception e) {
throw new IOException(e.getMessage(), e);
}
}
public static void upload(String baseDir, String fileName, MultipartFile file) throws IOException {
try {
// 获取后缀名称
String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath();
file.transferTo(Paths.get(absPath));
} catch (Exception e) {
throw new IOException(e.getMessage(), e);
}
}
public static File getAbsoluteFile(String uploadDir, String fileName) throws IOException {
File desc = new File(uploadDir + File.separator + fileName);
if (!desc.exists()) {
if (!desc.getParentFile().exists()) {
desc.getParentFile().mkdirs();
}
}
return desc;
}
// 动态检测内容类型
public static String detectContentType(String fileName) {
String ext = fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase();
switch (ext) {
case "mp3":
return "audio/mpeg";
case "wav":
return "audio/wav";
case "flac":
return "audio/flac";
case "mp4":
return "video/mp4";
case "pdf":
return "application/pdf";
case "jpg":
case "jpeg":
return "image/jpeg";
case "png":
return "image/png";
case "gif":
return "image/gif";
case "txt":
return "text/plain";
case "css":
return "text/css";
case "js":
return "application/javascript";
case "json":
return "application/json";
case "xml":
return "application/xml";
default:
return "application/octet-stream";
}
}
/**
* 类型(默认0其他 1文档 2图片 3视频)
*
* @param fileFormat 后缀格式
* @return 类型
*/
public static String getFileType(String fileFormat) {
String fileType = "0"; // 默认其他类型
// 检查文件类型
for (String format : documentFormats) {
if (format.equals(fileFormat)) {
fileType = "1";
break;
}
}
for (String format : imageFormats) {
if (format.equals(fileFormat)) {
fileType = "2";
break;
}
}
for (String format : videoFormats) {
if (format.equals(fileFormat)) {
fileType = "3";
break;
}
}
return fileType;
}
/**
* 更准确的文件大小格式化方法
*/
public static String formatFileSize(long size) {
try {
if (size <= 0) return "0 B";
final String[] units = new String[]{"B", "KB", "MB", "GB", "TB"};
int digitGroups = (int) (Math.log10(size) / Math.log10(1024));
// 确保不会超出数组范围
digitGroups = Math.min(digitGroups, units.length - 1);
return String.format("%.2f%s", size / Math.pow(1024, digitGroups), units[digitGroups]);
}catch (Exception e){
return "";
}
}
/**
* 下载或预览文件
* 支持音频调进度条
* @param response 响应对象
* @param fileName 文件名
* @param filePath 文件路径
* @param disposition attachment(默认下载)、inline(在线预览)
*/
public static void download(HttpServletRequest request,HttpServletResponse response, String fileName, String filePath, String disposition) {
Path basePath = Paths.get(filePath).toAbsolutePath().normalize();
if (!Files.exists(basePath)) {
throw new RuntimeException("文件不存在");
}
try {
response.setCharacterEncoding("utf-8");
response.setContentType(detectContentType(fileName));
String safeFilename = URLEncoder.encode(fileName, "UTF-8")
.replace("+", "%20");
String contentDispositionValue = disposition + "; filename=\"" + safeFilename + "\"";
response.setHeader("Content-Disposition", contentDispositionValue);
response.setHeader("Accept-Ranges", "bytes");
response.setHeader("Cache-Control", "no-cache");
long fileSize = Files.size(basePath);
String rangeHeader = request.getHeader("Range");
// 如果没有Range头,发送整个文件
if (rangeHeader == null) {
response.setHeader("Content-Length", String.valueOf(fileSize));
response.setStatus(HttpServletResponse.SC_OK);
Files.copy(basePath, response.getOutputStream());
//FileUtils.writeBytes(voice.getVoicePath(), response.getOutputStream());
return;
}
// 解析Range头
String[] ranges = rangeHeader.substring(6).split("-");
long start = Long.parseLong(ranges[0]);
long end = ranges.length > 1 ? Long.parseLong(ranges[1]) : fileSize - 1;
// 验证范围有效性
if (start < 0 || end >= fileSize || start > end) {
response.setStatus(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
response.setHeader("Content-Range", "bytes */" + fileSize);
return;
}
long contentLength = end - start + 1;
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
response.setHeader("Content-Range", "bytes " + start + "-" + end + "/" + fileSize);
response.setHeader("Content-Length", String.valueOf(contentLength));
// 使用RandomAccessFile读取文件的指定部分
try (RandomAccessFile randomAccessFile = new RandomAccessFile(basePath.toFile(), "r")) {
randomAccessFile.seek(start);
byte[] buffer = new byte[4096];
long remaining = contentLength;
while (remaining > 0) {
int read = randomAccessFile.read(buffer, 0, (int) Math.min(buffer.length, remaining));
if (read == -1) {
break;
}
response.getOutputStream().write(buffer, 0, read);
remaining -= read;
}
}
} catch (Exception e) {
log.error("下载或预览文件" + e.getMessage());
if (!response.isCommitted()) {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
}
}



—音频选择进度条是重新发送 request的Range字段,FileUtils.download方法已经处理了。
更多推荐

所有评论(0)