Plan: Chrome 无法显示上传图片的根因诊断与修复
TLDR: HTTP 200 已确认表明 Express.static 静态服务正确找到了文件。
真正的问题是:iOS 设备在"高效格式"摄像模式下拍摄的照片实际是 HEIC/HEIF 格式 ,上传时浏览器或 iOS 仍以 .jpeg 作为文件名发送------Chrome 不支持 HEIC 格式解码,因此显示破损黑屏。
macOS Preview 和 Safari 原生支持 HEIC,所以文件在宿主机上看起来"存在且正常",但 Chrome 无法渲染。
第一阶段:确认根因
步骤 1 --- 在宿主机终端执行,确认文件真实格式:
bash
file /文件路径/att-1774505932244-769917788.jpeg
若输出包含 HEIF image 或 ISO Media,则确认为 HEIC 格式。
步骤 2(可选确认) --- Chrome DevTools → Network → 找到该请求 → Response Headers → 确认 Content-Type: image/jpeg(MIME 类型是 jpeg,但内容是 HEIC 字节,Chrome 解码失败)。
第二阶段:修复
步骤 1 --- 安装 sharp 库(Node.js 图片处理,支持 HEIC→JPEG 转换):
bash
npm install sharp
npm install --save-dev @types/sharp
步骤 2 --- 修改 attachment.controller.ts
在 Multer 完成文件写入磁盘之后、入库之前,用 sharp 将 HEIC/HEIF 文件就地转换为标准 JPEG。核心逻辑:
- 判断
file.mimetype是否为image/heic或image/heif,或者检测文件头字节 - 若是 HEIC,则用
sharp(file.path).jpeg().toFile(tmpPath)转换后替换原文件 - 更新
file.mimetype为image/jpeg,file.size为转换后大小
步骤 3 --- 修改 main.ts
将 useStaticAssets 的相对路径改为绝对路径(Docker 环境最佳实践,避免 process.cwd() 在不同环境下解析不一致):
现状:
typescript
app.useStaticAssets('uploads', { prefix: '/uploads' });
改为(在文件顶部 import join):
typescript
import { join } from 'path';
// ...
app.useStaticAssets(join(__dirname, '..', 'uploads'), { prefix: '/uploads' });
__dirname 在 Docker 中为 /usr/src/app/dist,join(__dirname, '..', 'uploads') = /usr/src/app/uploads,与 Docker Volume 挂载路径完全一致。
步骤 4 --- 重新构建并启动容器:
bash
docker-compose up --build
相关文件
- main.ts --- 修改
useStaticAssets路径为绝对路径 - attachment.controller.ts --- 在
uploadFile方法中添加 HEIC→JPEG 转换逻辑
验证步骤
- 先执行
file命令确认 HEIC 假设成立 - 重建 Docker 镜像后,重新上传一张 iOS 照片
- 在 Chrome 中通过 URL 访问该图片,确认正常显示
决策说明
- 不修改 Multer 的
destination配置 :文件先存到uploads/attachments/,存入后立即原地转换,文件名保持不变,路径不受影响 - 对非图片文件(PDF 等):需在转换逻辑中判断 mimetype,跳过非图片类型
- 旧的损坏文件:无需处理,用户只需重新上传即可