Skip to content

Commit ee2e155

Browse files
committed
fix:修复云服务器部署音频播放异常问题
fix:修复文件上传错误问题
1 parent 9986807 commit ee2e155

File tree

4 files changed

+31
-126
lines changed

4 files changed

+31
-126
lines changed

src/main/java/com/xiaozhi/controller/FileUploadController.java

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,8 @@
55
import org.slf4j.Logger;
66
import org.slf4j.LoggerFactory;
77
import org.springframework.beans.factory.annotation.Value;
8-
import org.springframework.http.MediaType;
9-
import org.springframework.http.codec.multipart.FilePart;
108
import org.springframework.web.bind.annotation.*;
11-
import reactor.core.publisher.Mono;
9+
import org.springframework.web.multipart.MultipartFile;
1210

1311
import java.time.LocalDate;
1412
import java.time.format.DateTimeFormatter;
@@ -30,29 +28,28 @@ public class FileUploadController {
3028
/**
3129
* 通用文件上传方法
3230
*
33-
* @param filePart 上传的文件
31+
* @param file 上传的文件
3432
* @param type 文件类型(可选,用于分类存储)
3533
* @return 文件访问URL
3634
*/
37-
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
35+
@PostMapping("/upload")
3836
@ResponseBody
3937
public AjaxResult uploadFile(
40-
@RequestPart("file") FilePart filePart,
38+
@RequestParam("file") MultipartFile file,
4139
@RequestParam(value = "type", required = false, defaultValue = "common") String type) {
4240

4341
// 构建文件存储路径,按日期和类型分类
4442
String datePath = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
4543
String relativePath = type + "/" + datePath;
4644

4745
// 生成唯一文件名
48-
String originalFilename = filePart.filename();
46+
String originalFilename = file.getOriginalFilename();
4947
String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
5048
String fileName = UUID.randomUUID().toString().replaceAll("-", "") + extension;
5149

5250
// 使用FileUploadUtils进行智能上传(根据配置自动选择上传到本地或腾讯云COS)
53-
try{
54-
Mono<String> fileUrlMono = FileUploadUtils.smartUploadFilePart(uploadPath, relativePath, fileName, filePart);
55-
String fileUrl = fileUrlMono.block();
51+
try {
52+
String fileUrl = FileUploadUtils.smartUpload(uploadPath, relativePath, fileName, file);
5653
logger.info("文件上传成功: {}", fileUrl);
5754

5855
// 判断是否是COS URL
@@ -71,7 +68,7 @@ public AjaxResult uploadFile(
7168
}
7269

7370
return result;
74-
}catch (Exception e){
71+
} catch (Exception e) {
7572
logger.error("文件上传失败: {}", e.getMessage(), e);
7673
return AjaxResult.error("文件上传失败: " + e.getMessage());
7774
}

src/main/java/com/xiaozhi/utils/FileUploadUtils.java

Lines changed: 17 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,9 @@
77
import com.qcloud.cos.model.ObjectMetadata;
88
import com.qcloud.cos.model.PutObjectRequest;
99
import com.qcloud.cos.region.Region;
10-
import org.springframework.http.codec.multipart.FilePart;
1110
import org.springframework.web.multipart.MultipartFile;
12-
import reactor.core.publisher.Mono;
1311

1412
import java.io.File;
15-
import java.io.FileInputStream;
1613
import java.io.IOException;
1714
import java.io.InputStream;
1815
import java.nio.file.Files;
@@ -67,36 +64,6 @@ public static String uploadFile(String baseDir, String relativePath, String file
6764
return filePath.toString();
6865
}
6966

70-
/**
71-
* 上传FilePart文件
72-
*
73-
* @param baseDir 基础目录
74-
* @param relativePath 相对路径
75-
* @param fileName 文件名
76-
* @param filePart 文件部分
77-
* @return 包含文件完整路径的Mono
78-
*/
79-
public static Mono<String> uploadFilePart(String baseDir, String relativePath, String fileName, FilePart filePart) {
80-
// 创建目录
81-
String fullPath = baseDir + File.separator + relativePath;
82-
Path uploadPath = Paths.get(fullPath);
83-
84-
try {
85-
if (!Files.exists(uploadPath)) {
86-
Files.createDirectories(uploadPath);
87-
}
88-
89-
// 保存文件
90-
Path filePath = uploadPath.resolve(fileName);
91-
92-
// 使用FilePart的transferTo方法将内容写入目标文件
93-
return filePart.transferTo(filePath)
94-
.then(Mono.just(filePath.toString()));
95-
} catch (IOException e) {
96-
return Mono.error(e);
97-
}
98-
}
99-
10067
/**
10168
* 检查文件大小和类型
10269
*
@@ -109,16 +76,17 @@ public static void assertAllowed(MultipartFile file) {
10976
}
11077

11178
/**
112-
* 智能上传FilePart文件(根据配置决定上传到本地还是腾讯云COS)
79+
* 智能上传文件(根据配置决定上传到本地还是腾讯云COS)
11380
*
11481
* @param baseDir 本地基础目录
11582
* @param relativePath 相对路径
11683
* @param fileName 文件名
117-
* @param filePart 文件部分
118-
* @return 包含文件访问路径的Mono
84+
* @param file 文件
85+
* @return 文件访问路径
86+
* @throws IOException 如果上传过程中发生IO错误
11987
*/
120-
public static Mono<String> smartUploadFilePart(String baseDir, String relativePath, String fileName,
121-
FilePart filePart) {
88+
public static String smartUpload(String baseDir, String relativePath, String fileName,
89+
MultipartFile file) throws IOException {
12290
// 检查配置文件中是否有腾讯云COS的配置
12391
Properties properties = new Properties();
12492
try (InputStream inputStream = FileUploadUtils.class.getClassLoader()
@@ -144,90 +112,18 @@ public static Mono<String> smartUploadFilePart(String baseDir, String relativePa
144112
cosPath = cosPath + "/";
145113
}
146114

147-
// 先将FilePart保存到临时文件,然后上传到COS
148-
return uploadFilePartToTempAndThenToCos(filePart, bucketName, secretId, secretKey, region,
115+
// 上传到COS
116+
return uploadToCos(file, bucketName, secretId, secretKey, region,
149117
cosPath + relativePath + "/");
150118
}
151119
}
152120
} catch (Exception e) {
153121
// 如果读取配置或上传到COS出错,则回退到本地上传
154-
return uploadFilePart(baseDir, relativePath, fileName, filePart);
122+
return uploadFile(baseDir, relativePath, fileName, file);
155123
}
156124

157125
// 如果没有COS配置或配置不完整,则上传到本地
158-
return uploadFilePart(baseDir, relativePath, fileName, filePart);
159-
}
160-
161-
/**
162-
* 将FilePart先保存到临时文件,然后上传到腾讯云COS
163-
*
164-
* @param filePart 文件部分
165-
* @param bucketName 存储桶名称
166-
* @param secretId 腾讯云SecretId
167-
* @param secretKey 腾讯云SecretKey
168-
* @param region 地域信息
169-
* @param cosPath COS路径
170-
* @return 包含文件访问URL的Mono
171-
*/
172-
private static Mono<String> uploadFilePartToTempAndThenToCos(FilePart filePart, String bucketName, String secretId,
173-
String secretKey, String region, String cosPath) {
174-
try {
175-
// 创建临时文件
176-
Path tempFile = Files.createTempFile("cos_upload_", ".tmp");
177-
178-
// 将FilePart内容写入临时文件
179-
return filePart.transferTo(tempFile)
180-
.then(Mono.fromCallable(() -> {
181-
try {
182-
// 生成唯一文件名
183-
String originalFilename = filePart.filename();
184-
String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
185-
String fileName = UUID.randomUUID().toString().replaceAll("-", "") + suffix;
186-
187-
// 构建完整的对象键(Key)
188-
String key = cosPath + fileName;
189-
190-
// 创建 COSClient 实例
191-
COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
192-
ClientConfig clientConfig = new ClientConfig(new Region(region));
193-
COSClient cosClient = new COSClient(cred, clientConfig);
194-
195-
try {
196-
// 获取临时文件大小
197-
long fileSize = Files.size(tempFile);
198-
199-
// 上传文件
200-
ObjectMetadata metadata = new ObjectMetadata();
201-
metadata.setContentLength(fileSize);
202-
203-
// 尝试设置内容类型
204-
String contentType = determineContentType(originalFilename);
205-
if (contentType != null) {
206-
metadata.setContentType(contentType);
207-
}
208-
209-
try (InputStream fileInputStream = new FileInputStream(tempFile.toFile())) {
210-
PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key,
211-
fileInputStream, metadata);
212-
cosClient.putObject(putObjectRequest);
213-
}
214-
215-
// 生成文件访问URL
216-
return "https://" + bucketName + ".cos." + region + ".myqcloud.com/" + key;
217-
} finally {
218-
cosClient.shutdown();
219-
// 删除临时文件
220-
Files.deleteIfExists(tempFile);
221-
}
222-
} catch (Exception e) {
223-
// 确保临时文件被删除
224-
Files.deleteIfExists(tempFile);
225-
throw e;
226-
}
227-
}));
228-
} catch (IOException e) {
229-
return Mono.error(e);
230-
}
126+
return uploadFile(baseDir, relativePath, fileName, file);
231127
}
232128

233129
/**
@@ -303,7 +199,13 @@ public static String uploadToCos(MultipartFile file, String bucketName, String s
303199
// 上传文件
304200
ObjectMetadata metadata = new ObjectMetadata();
305201
metadata.setContentLength(file.getSize());
306-
metadata.setContentType(file.getContentType());
202+
203+
// 设置内容类型
204+
String contentType = file.getContentType();
205+
if (contentType == null || contentType.isEmpty()) {
206+
contentType = determineContentType(originalFilename);
207+
}
208+
metadata.setContentType(contentType);
307209

308210
PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, file.getInputStream(), metadata);
309211
cosClient.putObject(putObjectRequest);

src/main/resources/application.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ logging.level.root=INFO
2626
# 禁用Groovy模板位置检查
2727
spring.groovy.template.check-template-location=false
2828

29+
# 文件上传配置
30+
spring.servlet.multipart.enabled=true
2931
spring.servlet.multipart.max-file-size=2048MB
3032
spring.servlet.multipart.max-request-size=2048MB
3133

web/src/services/axios.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ axios.defaults.withCredentials = true;
1010
// 创建一个工具函数,用于处理静态资源URL
1111
export const getResourceUrl = (path) => {
1212
if (!path) return '';
13+
14+
if (path.startsWith('http://') || path.startsWith('https://')) {
15+
return path;
16+
}
1317

1418
// 确保URL以/开头
1519
if (!path.startsWith('/')) {

0 commit comments

Comments
 (0)