@@ -168,7 +168,7 @@ marked.setOptions({
168
168
<template v-slot :activator =" { props } " >
169
169
<v-btn v-bind =" props" @click =" sendMessage" class =" send-btn" icon =" mdi-send"
170
170
variant =" text" color =" deep-purple"
171
- :disabled =" !prompt && stagedImagesUrl .length === 0 && !stagedAudioUrl" />
171
+ :disabled =" !prompt && stagedImagesName .length === 0 && !stagedAudioUrl" />
172
172
</template >
173
173
</v-tooltip >
174
174
@@ -218,7 +218,8 @@ export default {
218
218
messages: [],
219
219
conversations: [],
220
220
currCid: ' ' ,
221
- stagedImagesUrl: [],
221
+ stagedImagesName: [], // 用于存储图片**文件名**的数组
222
+ stagedImagesUrl: [], // 用于存储图片的blob URL数组
222
223
loadingChat: false ,
223
224
224
225
inputFieldLabel: ' 聊天吧!' ,
@@ -236,7 +237,9 @@ export default {
236
237
// Ctrl键长按相关变量
237
238
ctrlKeyDown: false ,
238
239
ctrlKeyTimer: null ,
239
- ctrlKeyLongPressThreshold: 300 // 长按阈值,单位毫秒
240
+ ctrlKeyLongPressThreshold: 300 , // 长按阈值,单位毫秒
241
+
242
+ mediaCache: {}, // Add a cache to store media blobs
240
243
}
241
244
},
242
245
@@ -265,9 +268,31 @@ export default {
265
268
266
269
// 移除keyup事件监听
267
270
document .removeEventListener (' keyup' , this .handleInputKeyUp );
271
+
272
+ // Cleanup blob URLs
273
+ this .cleanupMediaCache ();
268
274
},
269
275
270
276
methods: {
277
+ async getMediaFile (filename ) {
278
+ if (this .mediaCache [filename]) {
279
+ return this .mediaCache [filename];
280
+ }
281
+
282
+ try {
283
+ const response = await axios .get (' /api/chat/get_file' , {
284
+ params: { filename },
285
+ responseType: ' blob'
286
+ });
287
+
288
+ const blobUrl = URL .createObjectURL (response .data );
289
+ this .mediaCache [filename] = blobUrl;
290
+ return blobUrl;
291
+ } catch (error) {
292
+ console .error (' Error fetching media file:' , error);
293
+ return ' ' ;
294
+ }
295
+ },
271
296
272
297
async startListeningEvent () {
273
298
const response = await fetch (' /api/chat/listen' , {
@@ -328,17 +353,19 @@ export default {
328
353
329
354
if (chunk_json .type === ' image' ) {
330
355
let img = chunk_json .data .replace (' [IMAGE]' , ' ' );
356
+ const imageUrl = await this .getMediaFile (img);
331
357
let bot_resp = {
332
358
type: ' bot' ,
333
- message: ` <img src="/api/chat/get_file?filename= ${ img } " style="max-width: 80%; border-radius: 8px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);"/>`
359
+ message: ` <img src="${ imageUrl } " style="max-width: 80%; border-radius: 8px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);"/>`
334
360
}
335
361
this .messages .push (bot_resp);
336
362
} else if (chunk_json .type === ' record' ) {
337
363
let audio = chunk_json .data .replace (' [RECORD]' , ' ' );
364
+ const audioUrl = await this .getMediaFile (audio);
338
365
let bot_resp = {
339
366
type: ' bot' ,
340
367
message: ` <audio controls class="audio-player">
341
- <source src="/api/chat/get_file?filename= ${ audio } " type="audio/wav">
368
+ <source src="${ audioUrl } " type="audio/wav">
342
369
您的浏览器不支持音频播放。
343
370
</audio>`
344
371
}
@@ -403,15 +430,14 @@ export default {
403
430
try {
404
431
const response = await axios .post (' /api/chat/post_file' , formData, {
405
432
headers: {
406
- ' Content-Type' : ' multipart/form-data' ,
407
- ' Authorization' : ' Bearer ' + localStorage .getItem (' token' )
433
+ ' Content-Type' : ' multipart/form-data'
408
434
}
409
435
});
410
436
411
437
const audio = response .data .data .filename ;
412
438
console .log (' Audio uploaded:' , audio);
413
439
414
- this .stagedAudioUrl = ` /api/chat/get_file? filename= ${ audio } ` ;
440
+ this .stagedAudioUrl = audio; // Store just the filename
415
441
} catch (err) {
416
442
console .error (' Error uploading audio:' , err);
417
443
}
@@ -430,13 +456,13 @@ export default {
430
456
try {
431
457
const response = await axios .post (' /api/chat/post_image' , formData, {
432
458
headers: {
433
- ' Content-Type' : ' multipart/form-data' ,
434
- ' Authorization' : ' Bearer ' + localStorage .getItem (' token' )
459
+ ' Content-Type' : ' multipart/form-data'
435
460
}
436
461
});
437
462
438
463
const img = response .data .data .filename ;
439
- this .stagedImagesUrl .push (` /api/chat/get_file?filename=${ img} ` );
464
+ this .stagedImagesName .push (img); // Store just the filename
465
+ this .stagedImagesUrl .push (URL .createObjectURL (file)); // Create a blob URL for immediate display
440
466
441
467
} catch (err) {
442
468
console .error (' Error uploading image:' , err);
@@ -446,6 +472,7 @@ export default {
446
472
},
447
473
448
474
removeImage (index ) {
475
+ this .stagedImagesName .splice (index, 1 );
449
476
this .stagedImagesUrl .splice (index, 1 );
450
477
},
451
478
@@ -462,28 +489,30 @@ export default {
462
489
getConversationMessages (cid ) {
463
490
if (! cid[0 ])
464
491
return ;
465
- axios .get (' /api/chat/get_conversation?conversation_id=' + cid[0 ]).then (response => {
492
+ axios .get (' /api/chat/get_conversation?conversation_id=' + cid[0 ]).then (async response => {
466
493
this .currCid = cid[0 ];
467
494
let message = JSON .parse (response .data .data .history );
468
495
for (let i = 0 ; i < message .length ; i++ ) {
469
496
if (message[i].message .startsWith (' [IMAGE]' )) {
470
497
let img = message[i].message .replace (' [IMAGE]' , ' ' );
471
- message[i].message = ` <img src="/api/chat/get_file?filename=${ img} " style="max-width: 80%; border-radius: 8px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);"/>`
498
+ const imageUrl = await this .getMediaFile (img);
499
+ message[i].message = ` <img src="${ imageUrl} " style="max-width: 80%; border-radius: 8px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);"/>`
472
500
}
473
501
if (message[i].message .startsWith (' [RECORD]' )) {
474
502
let audio = message[i].message .replace (' [RECORD]' , ' ' );
503
+ const audioUrl = await this .getMediaFile (audio);
475
504
message[i].message = ` <audio controls class="audio-player">
476
- <source src="/api/chat/get_file?filename= ${ audio } " type="audio/wav">
505
+ <source src="${ audioUrl } " type="audio/wav">
477
506
您的浏览器不支持音频播放。
478
507
</audio>`
479
508
}
480
509
if (message[i].image_url && message[i].image_url .length > 0 ) {
481
510
for (let j = 0 ; j < message[i].image_url .length ; j++ ) {
482
- message[i].image_url [j] = ` /api/chat/get_file?filename= ${ message[i].image_url [j]} ` ;
511
+ message[i].image_url [j] = await this . getMediaFile ( message[i].image_url [j]) ;
483
512
}
484
513
}
485
514
if (message[i].audio_url ) {
486
- message[i].audio_url = ` /api/chat/get_file?filename= ${ message[i].audio_url } ` ;
515
+ message[i].audio_url = await this . getMediaFile ( message[i].audio_url ) ;
487
516
}
488
517
}
489
518
this .messages = message;
@@ -534,31 +563,40 @@ export default {
534
563
await this .newConversation ();
535
564
}
536
565
537
- this .messages .push ({
566
+ // Create a message object with actual URLs for display
567
+ const userMessage = {
538
568
type: ' user' ,
539
569
message: this .prompt ,
540
- image_url: this .stagedImagesUrl ,
541
- audio_url: this .stagedAudioUrl
542
- });
543
-
544
- this .scrollToBottom ();
570
+ image_url: [],
571
+ audio_url: null
572
+ };
545
573
546
- // images
547
- let image_filenames = [];
548
- for (let i = 0 ; i < this .stagedImagesUrl .length ; i++ ) {
549
- let img = this .stagedImagesUrl [i].replace (' /api/chat/get_file?filename=' , ' ' );
550
- image_filenames .push (img);
574
+ // Convert image filenames to blob URLs for display
575
+ if (this .stagedImagesName .length > 0 ) {
576
+ for (let i = 0 ; i < this .stagedImagesName .length ; i++ ) {
577
+ // If it's just a filename, get the blob URL
578
+ if (! this .stagedImagesName [i].startsWith (' blob:' )) {
579
+ const imgUrl = await this .getMediaFile (this .stagedImagesName [i]);
580
+ userMessage .image_url .push (imgUrl);
581
+ } else {
582
+ userMessage .image_url .push (this .stagedImagesName [i]);
583
+ }
584
+ }
551
585
}
552
586
553
- // audio
554
- let audio_filenames = [];
587
+ // Convert audio filename to blob URL for display
555
588
if (this .stagedAudioUrl ) {
556
- let audio = this .stagedAudioUrl .replace (' /api/chat/get_file?filename=' , ' ' );
557
- audio_filenames .push (audio);
589
+ if (! this .stagedAudioUrl .startsWith (' blob:' )) {
590
+ userMessage .audio_url = await this .getMediaFile (this .stagedAudioUrl );
591
+ } else {
592
+ userMessage .audio_url = this .stagedAudioUrl ;
593
+ }
558
594
}
559
595
560
- this .loadingChat = true ;
596
+ this .messages .push (userMessage);
597
+ this .scrollToBottom ();
561
598
599
+ this .loadingChat = true ;
562
600
563
601
fetch (' /api/chat/send' , {
564
602
method: ' POST' ,
@@ -569,20 +607,19 @@ export default {
569
607
body: JSON .stringify ({
570
608
message: this .prompt ,
571
609
conversation_id: this .currCid ,
572
- image_url: image_filenames,
573
- audio_url: audio_filenames
574
- }) // 发送请求体
575
- })
576
- .then (response => {
577
- this .prompt = ' ' ;
578
- this .stagedImagesUrl = [];
579
- this .stagedAudioUrl = " " ;
580
-
581
- this .loadingChat = false ;
610
+ image_url: this .stagedImagesName , // Already contains just filenames
611
+ audio_url: this .stagedAudioUrl ? [this .stagedAudioUrl ] : [] // Already contains just filename
582
612
})
583
- .catch (err => {
584
- console .error (err);
585
- });
613
+ })
614
+ .then (response => {
615
+ this .prompt = ' ' ;
616
+ this .stagedImagesName = [];
617
+ this .stagedAudioUrl = " " ;
618
+ this .loadingChat = false ;
619
+ })
620
+ .catch (err => {
621
+ console .error (err);
622
+ });
586
623
},
587
624
scrollToBottom () {
588
625
this .$nextTick (() => {
@@ -623,6 +660,15 @@ export default {
623
660
}
624
661
}
625
662
},
663
+
664
+ cleanupMediaCache () {
665
+ Object .values (this .mediaCache ).forEach (url => {
666
+ if (url .startsWith (' blob:' )) {
667
+ URL .revokeObjectURL (url);
668
+ }
669
+ });
670
+ this .mediaCache = {};
671
+ },
626
672
},
627
673
}
628
674
</script >
0 commit comments