chengjie 2 天之前
父節點
當前提交
c1fb4582ce
共有 1 個文件被更改,包括 62 次插入56 次删除
  1. 62 56
      src/api/common/commonController.js

+ 62 - 56
src/api/common/commonController.js

@@ -18,19 +18,14 @@ const ALLOWED_IMAGE_TYPES = new Set([
18 18
     'jpg', 'jpeg', 'png', 'bmp', 'heic', 'heif', 'webp', 'gif'
19 19
 ]);
20 20
 
21
-// 不再允许音频类型
22
-// const ALLOWED_AUDIO_TYPES = new Set([
23
-//     'aac', 'mp3', 'mp4', 'm4a', 'flac', 'ogg', 'ape', 'amr', 
24
-//     'wma', 'wav', 'aiff', 'caf'
25
-// ]);
21
+const ALLOWED_AUDIO_TYPES = new Set([
22
+    'aac', 'mp3', 'mp4', 'm4a', 'flac', 'ogg', 'ape', 'amr',
23
+    'wma', 'wav', 'aiff', 'caf'
24
+]);
26 25
 
27
-// 阻止的文件类型,包括视频和其他不允许的类型
28 26
 const BLOCKED_TYPES = new Set([
29
-    'php', 'js', 'txt', 
30
-    // 视频格式
31
-    'mp4', 'avi', 'mov', 'wmv', 'flv', 'mkv', 'webm', '3gp', 'm4v',
32
-    // 音频格式
33
-    'aac', 'mp3', 'm4a', 'flac', 'ogg', 'ape', 'amr', 'wma', 'wav', 'aiff', 'caf'
27
+    'php', 'js', 'txt',
28
+    'avi', 'mov', 'wmv', 'flv', 'mkv', 'webm', '3gp', 'm4v'
34 29
 ]);
35 30
 
36 31
 // 文件上传配置
@@ -39,10 +34,10 @@ const fileFilter = (req, file, cb) => {
39 34
     
40 35
     if (BLOCKED_TYPES.has(extension)) {
41 36
         cb(new Error('不允许上传该类型的文件!'), false);
42
-    } else if (ALLOWED_IMAGE_TYPES.has(extension)) {
37
+    } else if (ALLOWED_IMAGE_TYPES.has(extension) || ALLOWED_AUDIO_TYPES.has(extension)) {
43 38
         cb(null, true);
44 39
     } else {
45
-        cb(new Error('只允许上传图片文件!'), false);
40
+        cb(new Error('只允许上传图片或音频文件!'), false);
46 41
     }
47 42
 };
48 43
 
@@ -70,8 +65,10 @@ export const UploadFile = async (ctx) => {
70 65
 
71 66
         if (ALLOWED_IMAGE_TYPES.has(extension)) {
72 67
             await checkImage(filename, filepath);
68
+        } else if (ALLOWED_AUDIO_TYPES.has(extension)) {
69
+            await uploadFile(filename, filepath);
73 70
         } else {
74
-            throw new Error('只允许上传图片文件!');
71
+            throw new Error('只允许上传图片或音频文件!');
75 72
         }
76 73
 
77 74
         ctx.body = {
@@ -134,7 +131,11 @@ async function checkImage(filename, filepath) {
134 131
                 if (size.width > maxSize || size.height > maxSize) {
135 132
                     imageMagick(filepath)
136 133
                         .resize(maxSize, maxSize)
137
-                        .write(filepath, async () => {
134
+                        .write(filepath, async (writeError) => {
135
+                            if (writeError) {
136
+                                reject(writeError);
137
+                                return;
138
+                            }
138 139
                             try {
139 140
                                 await uploadImage(filename, filepath);
140 141
                                 resolve();
@@ -152,54 +153,59 @@ async function checkImage(filename, filepath) {
152 153
     }
153 154
 }
154 155
 
155
-// 上传图片到腾讯云
156
-async function uploadImage(filename, filepath) {
156
+const TRANSIENT_UPLOAD_ERRORS = new Set([
157
+    'ECONNRESET', 'ETIMEDOUT', 'EPIPE', 'ECONNREFUSED'
158
+]);
159
+
160
+function shouldRetryUpload(error) {
161
+    return TRANSIENT_UPLOAD_ERRORS.has(error?.code)
162
+        || Number(error?.statusCode) >= 500;
163
+}
164
+
165
+async function uploadToCloud(filename, filepath) {
157 166
     const cos = new COS(config.QCloud);
158
-    
159
-    const params = {
160
-        Bucket: 'miaguo-1253256735',
161
-        Region: 'ap-guangzhou',
162
-        Key: filename,
163
-        Body: fs.createReadStream(filepath),
164
-        ContentLength: fs.statSync(filepath).size
165
-    };
167
+    const contentLength = fs.statSync(filepath).size;
168
+    let lastError;
166 169
 
167
-    return new Promise((resolve, reject) => {
168
-        cos.putObject(params, (err, data) => {
169
-            if (err) {
170
-                reject(err);
171
-            } else {
172
-                fsPromises.unlink(filepath)
173
-                    .then(() => resolve(data))
174
-                    .catch(reject);
170
+    for (let attempt = 0; attempt < 2; attempt++) {
171
+        try {
172
+            const data = await new Promise((resolve, reject) => {
173
+                cos.putObject({
174
+                    Bucket: 'miaguo-1253256735',
175
+                    Region: 'ap-guangzhou',
176
+                    Key: filename,
177
+                    Body: fs.createReadStream(filepath),
178
+                    ContentLength: contentLength
179
+                }, (error, result) => {
180
+                    if (error) reject(error);
181
+                    else resolve(result);
182
+                });
183
+            });
184
+
185
+            try {
186
+                await fsPromises.unlink(filepath);
187
+            } catch (cleanupError) {
188
+                console.warn('Failed to clean up uploaded temp file:', cleanupError.message);
175 189
             }
176
-        });
177
-    });
190
+            return data;
191
+        } catch (error) {
192
+            lastError = error;
193
+            if (attempt >= 1 || !shouldRetryUpload(error)) throw error;
194
+            await new Promise(resolve => setTimeout(resolve, 600));
195
+        }
196
+    }
197
+
198
+    throw lastError;
199
+}
200
+
201
+// 上传图片到腾讯云
202
+async function uploadImage(filename, filepath) {
203
+    return uploadToCloud(filename, filepath);
178 204
 }
179 205
 
180 206
 // 上传文件到腾讯云
181 207
 async function uploadFile(filename, filepath) {
182
-    const cos = new COS(config.QCloud);
183
-    
184
-    const params = {
185
-        Bucket: 'miaguo-1253256735',
186
-        Region: 'ap-guangzhou',
187
-        Key: filename,
188
-        Body: fs.createReadStream(filepath),
189
-        ContentLength: fs.statSync(filepath).size
190
-    };
191
-
192
-    return new Promise((resolve, reject) => {
193
-        cos.putObject(params, (err, data) => {
194
-            if (err) {
195
-                reject(err);
196
-            } else {
197
-                fsPromises.unlink(filepath)
198
-                    .then(() => resolve(data))
199
-                    .catch(reject);
200
-            }
201
-        });
202
-    });
208
+    return uploadToCloud(filename, filepath);
203 209
 }
204 210
 
205 211
 export async function GetBaiduToken (ctx) {