使用AWS GetObject从私有S3桶获取图片并在浏览器展示失败求助
问题:从S3私有桶读取图片并在Next.js中展示失败
使用@aws-sdk/client-s3工具从AWS S3私有存储桶读取图片,通过Next.js API接口获取后,无法在浏览器<img>标签中正常展示,生成的Base64内容包含大量异常字符,无法解析。
后端API代码(原错误版本)
import { GetObjectCommand, S3Client } from "@aws-sdk/client-s3"; export const getObject = async () => { const s3Client = new S3Client({ region: process.env.AWS_REGION, credentials: { accessKeyId: process.env.AWS_ACCESS_KEY, secretAccessKey: process.env.AWS_SECRET_KEY, }, }) console.log('GET OBJECT FUNC') try { // Create a helper function to convert a ReadableStream to a string. const streamToString = (stream) => new Promise((resolve, reject) => { const chunks = []; stream.on("data", (chunk) => chunks.push(chunk)); stream.on("error", reject); stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf8"))); }); // Get the object from the Amazon S3 bucket. It is returned as a ReadableStream. const data = await s3Client.send(new GetObjectCommand({ Bucket: process.env.AWS_BUCKET, Delimiter: "/", Key: process.env.AWS_BUCKET_FOLDER + 'Screenshot from 2022-02-09 08-13-26.png' })); // return data; // For unit tests. // Convert the ReadableStream to a string. const bodyContents = await streamToString(data.Body); return bodyContents; } catch (err) { console.log("Error", err); } }; export default async function handler(request, response) { if (request.method === "GET") { const data = await getObject() return response.status(200).json(data); } else { return response.status(405).end(); } }
前端请求代码(原错误版本)
const getImage = async (event) => { console.log('GET IMAGE') let arrayBuffer = await axios.get("/api/get-object", {responseType: 'arraybuffer'}) console.log(arrayBuffer.data) let base64 = 'data:image/png;base64,' + Buffer.from(arrayBuffer.data).toString('base64') console.log(base64) event.target.src = base64 }
问题现象
后端将图片二进制流转成UTF-8字符串后返回,前端接收后转Base64时出现大量异常字符(如/ve+、转义斜杠等),导致<img>无法识别并展示图片。
解决方案
1. 修改后端API:直接返回二进制流
核心错误是将二进制图片转成UTF-8字符串,破坏了原始数据。修改后直接返回图片流并设置正确的Content-Type:
import { GetObjectCommand, S3Client } from "@aws-sdk/client-s3"; import { pipeline } from "stream/promises"; export const getObject = async () => { const s3Client = new S3Client({ region: process.env.AWS_REGION, credentials: { accessKeyId: process.env.AWS_ACCESS_KEY, secretAccessKey: process.env.AWS_SECRET_KEY, }, }) try { const data = await s3Client.send(new GetObjectCommand({ Bucket: process.env.AWS_BUCKET, Key: process.env.AWS_BUCKET_FOLDER + 'Screenshot from 2022-02-09 08-13-26.png' })); return data; } catch (err) { console.log("Error", err); throw err; } }; export default async function handler(request, response) { if (request.method === "GET") { try { const data = await getObject(); // 从S3返回的元数据中获取正确的Content-Type,没有则默认png response.setHeader('Content-Type', data.ContentType || 'image/png'); // 将S3的可读流直接管道到响应流 await pipeline(data.Body, response); } catch (err) { response.status(500).json({ error: err.message }); } } else { response.status(405).end(); } }
2. 简化前端代码:直接使用API地址作为图片src
不需要转Base64,浏览器会自动处理二进制流:
const getImage = (event) => { event.target.src = "/api/get-object"; // 可选:监听加载状态 event.target.onload = () => console.log("图片加载成功"); event.target.onerror = () => console.log("图片加载失败"); }
原理说明
图片是二进制文件,不能用UTF-8文本编码处理,否则会丢失或损坏原始字节。直接返回二进制流并设置正确的Content-Type后,浏览器可以直接识别并渲染图片,无需额外转码操作。
内容的提问来源于stack exchange,提问作者Richard Bridge
相关产品推荐
相关产品推荐

