# MIME 类型检测说明 ## 问题 后端下载接口返回的 MIME 类型是 `application/octet-stream`,但 Clipboard API 只支持图片类型: ``` Failed to execute 'write' on 'Clipboard': Type application/octet-stream not supported on write. ``` ## 原因 后端代码设置了通用的二进制流类型: ```java response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8"); ``` Clipboard API 只接受以下图片 MIME 类型: - `image/png` - `image/jpeg` - `image/gif` - `image/bmp` - `image/webp` - `image/svg+xml` ## 解决方案 在前端通过读取文件头(Magic Number)来检测真实的图片类型,然后创建正确 MIME 类型的 Blob。 ### 实现代码 ```typescript // 1. 下载图片 const blob = await downloadSignature(ossId); // 2. 检测图片真实类型 let imageBlob = blob; let mimeType = blob.type; // 如果是 application/octet-stream,需要检测真实的图片类型 if (mimeType === 'application/octet-stream' || !mimeType.startsWith('image/')) { // 读取文件头 const arrayBuffer = await blob.arrayBuffer(); const uint8Array = new Uint8Array(arrayBuffer); // 检测文件头(Magic Number) if (uint8Array[0] === 0xFF && uint8Array[1] === 0xD8 && uint8Array[2] === 0xFF) { mimeType = 'image/jpeg'; } else if (uint8Array[0] === 0x89 && uint8Array[1] === 0x50 && uint8Array[2] === 0x4E && uint8Array[3] === 0x47) { mimeType = 'image/png'; } else if (uint8Array[0] === 0x47 && uint8Array[1] === 0x49 && uint8Array[2] === 0x46) { mimeType = 'image/gif'; } else if (uint8Array[0] === 0x42 && uint8Array[1] === 0x4D) { mimeType = 'image/bmp'; } else if (uint8Array[0] === 0x52 && uint8Array[1] === 0x49 && uint8Array[2] === 0x46 && uint8Array[3] === 0x46) { mimeType = 'image/webp'; } else { // 默认使用 PNG mimeType = 'image/png'; } // 创建新的 Blob,使用正确的 MIME 类型 imageBlob = new Blob([arrayBuffer], { type: mimeType }); } // 3. 复制到剪贴板 await navigator.clipboard.write([ new ClipboardItem({ [imageBlob.type]: imageBlob }) ]); ``` ## 文件头(Magic Number)对照表 | 格式 | 文件头(十六进制) | 说明 | |------|-------------------|------| | JPEG | `FF D8 FF` | JPEG 图片 | | PNG | `89 50 4E 47` | PNG 图片(\x89PNG) | | GIF | `47 49 46` | GIF 图片(GIF) | | BMP | `42 4D` | BMP 图片(BM) | | WebP | `52 49 46 46` | WebP 图片(RIFF) | | SVG | `3C 73 76 67` | SVG 图片(