jack 1 mēnesi atpakaļ
vecāks
revīzija
db4210cd72
1 mainītis faili ar 71 papildinājumiem un 70 dzēšanām
  1. 71 70
      src/hooks/useImport.ts

+ 71 - 70
src/hooks/useImport.ts

@@ -402,18 +402,19 @@ export default () => {
   /**
    * 将 base64 字符串或 Blob 转换为 File 对象
    */
-  const dataToFile = (data: string | Blob, filename: string, mimeType?: string): File => {
+  const dataToFile = async (data: string | Blob, filename: string, videoMimeType: string): File => {
     if (typeof data === 'string') {
-      // 移除可能的 data:image/png;base64, 前缀
-      const base64Data = data.includes('base64,') ? data.split('base64,')[1] : data
-      const byteCharacters = atob(base64Data)
-      const byteNumbers = new Array(byteCharacters.length)
-      for (let i = 0; i < byteCharacters.length; i++) {
-        byteNumbers[i] = byteCharacters.charCodeAt(i)
+      // 1. 通过 fetch 获取 Blob 数据
+      const response = await fetch(data);
+      if (!response.ok) {
+        throw new Error(`Failed to fetch blob: ${response.statusText}`);
       }
-      const byteArray = new Uint8Array(byteNumbers)
-      const blob = new Blob([byteArray], { type: mimeType || 'image/png' })
-      return new File([blob], filename, { type: blob.type })
+      const blob = await response.blob();
+
+      // 2. 将 Blob 转换为 File 对象
+      //    如果原 Blob 有 type,会自动保留;否则可手动指定 videoMimeType
+      const file = new File([blob], filename, { type: videoMimeType || blob.type });
+      return file;
     }
     else if (data instanceof Blob) {
       return new File([data], filename, { type: data.type })
@@ -512,11 +513,11 @@ export default () => {
     options?: { tolerance?: number }
   ): Promise<File> => {
     const tolerance = options?.tolerance ?? 15; // 只处理非常接近白色的像素
-  
+
     // 1. 将输入统一转为可加载的 URL
     let imageUrl: string;
     let needRevoke = false;
-  
+
     if (typeof data === 'string') {
       imageUrl = data.startsWith('data:') ? data : `data:image/png;base64,${data}`;
     } else if (data instanceof Blob) {
@@ -525,67 +526,67 @@ export default () => {
     } else {
       throw new Error('Unsupported data type: expected string or Blob');
     }
-  
+    // 2. 加载图像
+    const img = await new Promise<HTMLImageElement>((resolve, reject) => {
+      const image = new Image();
+      image.onload = () => resolve(image);
+      image.onerror = reject;
+      if (typeof data === 'string' && !data.startsWith('data:')) {
+        image.crossOrigin = 'anonymous';
+      }
+      image.src = imageUrl;
+    });
+    const canvas = document.createElement('canvas');
     try {
-      // 2. 加载图像
-      const img = await new Promise<HTMLImageElement>((resolve, reject) => {
-        const image = new Image();
-        image.onload = () => resolve(image);
-        image.onerror = reject;
-        if (typeof data === 'string' && !data.startsWith('data:')) {
-          image.crossOrigin = 'anonymous';
-        }
-        image.src = imageUrl;
-      });
-  
       // 3. 绘制到 Canvas
-      const canvas = document.createElement('canvas');
       canvas.width = img.width;
       canvas.height = img.height;
       const ctx = canvas.getContext('2d')!;
       ctx.drawImage(img, 0, 0);
-  
+
       // 4. 获取像素数据
       const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
       const dataArray = imageData.data;
-  
+
       // 5. 遍历像素:只将白色背景区域设为透明,其他颜色原样保留
       for (let i = 0; i < dataArray.length; i += 4) {
         const r = dataArray[i];
         const g = dataArray[i + 1];
         const b = dataArray[i + 2];
-  
+
         // 计算与纯白色 (255,255,255) 的欧几里得距离
         const dr = r - 255;
         const dg = g - 255;
         const db = b - 255;
         const dist = Math.sqrt(dr * dr + dg * dg + db * db);
-  
+
         if (dist <= tolerance) {
           // 非常接近白色 → 设为全透明
           dataArray[i + 3] = 0;
         }
         // 其他所有像素(包括浅蓝、灰色、黑色等)保持原样,不修改颜色和透明度
       }
-  
+
       // 6. 将修改后的像素放回 Canvas
       ctx.putImageData(imageData, 0, 0);
-  
-      // 7. 导出为 PNG Blob
-      const outputBlob = await new Promise<Blob>((resolve, reject) => {
-        canvas.toBlob((blob) => {
-          if (blob) resolve(blob);
-          else reject(new Error('Canvas toBlob failed'));
-        }, 'image/png');
-      });
-  
-      // 8. 返回 File 对象
-      return new File([outputBlob], filename, { type: 'image/png' });
-    } finally {
+
+
+    }
+    finally {
       if (needRevoke) {
         URL.revokeObjectURL(imageUrl);
       }
     }
+    // 7. 导出为 PNG Blob
+    const outputBlob = await new Promise<Blob>((resolve, reject) => {
+      canvas.toBlob((blob) => {
+        if (blob) resolve(blob);
+        else reject(new Error('Canvas toBlob failed'));
+      }, 'image/png');
+    });
+
+    // 8. 返回 File 对象
+    return new File([outputBlob], filename, { type: 'image/png' });
   };
 
   /**
@@ -1383,11 +1384,12 @@ export default () => {
                 const uploadTask = (async () => {
                   try {
                     const file = await makeWhiteTransparent(el.src, `image_${Date.now()}.png`)
-                    const url = await uploadFileToS3(file)
-                    element.src = url // 替换为远程 URL
-                    
-                    const slidesStore = useSlidesStore()
-                    slidesStore.updateElement({ id: element.id, props: { src: url } })
+                    if (file) {
+                      const url = await uploadFileToS3(file)
+                      element.src = url // 替换为远程 URL
+                      const slidesStore = useSlidesStore()
+                      slidesStore.updateElement({ id: element.id, props: { src: url } })
+                    }
                   }
                   catch (error) {
                     console.error('Image upload failed:', error)
@@ -1416,12 +1418,13 @@ export default () => {
               if (el.picBase64 && typeof el.picBase64 === 'string' && el.picBase64.startsWith('data:')) {
                 const uploadTask = (async () => {
                   try {
-                    const file = makeWhiteTransparent(el.picBase64, `image_${Date.now()}.png`, 'image/png')
-                    const url = await uploadFileToS3(file)
-                    element.src = url // 替换为远程 URL
-                    
-                    const slidesStore = useSlidesStore()
-                    slidesStore.updateElement({ id: element.id, props: { src: url } })
+                    const file = makeWhiteTransparent(el.picBase64, `image_${Date.now()}.png`)
+                    if (file) {
+                      const url = await uploadFileToS3(file)
+                      element.src = url // 替换为远程 URL
+                      const slidesStore = useSlidesStore()
+                      slidesStore.updateElement({ id: element.id, props: { src: url } })
+                    }
                   }
                   catch (error) {
                     console.error('Image upload failed:', error)
@@ -1451,16 +1454,18 @@ export default () => {
                 loop: false,
                 autoplay: false,
               }
+              const localData = el.blob || (el.src && typeof el.src === 'string' && el.src.startsWith('data:') ? el.src : null)
 
-              if (el.blob instanceof Blob) {
+              if (localData) {
                 const uploadTask = (async () => {
                   try {
-                    const file = dataToFile(el.blob, `audio_${Date.now()}.mp3`, el.blob.type)
-                    const url = await uploadFileToS3(file)
-                    element.src = url
-
-                    const slidesStore = useSlidesStore()
-                    slidesStore.updateElement({ id: element.id, props: { src: url } })
+                    const file = await dataToFile(localData, `audio_${Date.now()}.mp3`, 'audio/mpeg')
+                    if (file) {
+                      const url = await uploadFileToS3(file)
+                      element.src = url
+                      const slidesStore = useSlidesStore()
+                      slidesStore.updateElement({ id: element.id, props: { src: url } })
+                    }
                   }
                   catch (error) {
                     console.error('Audio upload failed:', error)
@@ -1491,17 +1496,13 @@ export default () => {
                 const uploadTask = (async () => {
                   try {
                     let file: File
-                    if (localData instanceof Blob) {
-                      file = dataToFile(localData, `video_${Date.now()}.mp4`, localData.type)
-                    }
-                    else {
-                      file = dataToFile(localData, `video_${Date.now()}.mp4`, 'video/mp4')
+                    file = await dataToFile(localData, `video_${Date.now()}.mp4`, 'video/mp4')
+                    if (file) {
+                      const url = await uploadFileToS3(file)
+                      element.src = url
+                      const slidesStore = useSlidesStore()
+                      slidesStore.updateElement({ id: element.id, props: { src: url } })
                     }
-                    const url = await uploadFileToS3(file)
-                    element.src = url
-                    
-                    const slidesStore = useSlidesStore()
-                    slidesStore.updateElement({ id: element.id, props: { src: url } })
                   }
                   catch (error) {
                     console.error('Video upload failed:', error)