【问题标题】:Image file is not viewing uploaded by s3s3 上传的图片文件未查看
【发布时间】:2021-12-17 00:00:38
【问题描述】:

我正在使用 s3 上传图像文件。但是每当我尝试使用 URL 以及从 s3 控制台下载时,都会下载图像文件。但是,它不会对图像查看器可见。它只是显示不兼容的文件类型。

myS3Function.uploadFile(request.body.fileName, request.files.myFileData, "image_folder").then(filename => {
 //success
})

.

const AWS = require('aws-sdk');

const s3 = new AWS.S3({
 region: process.env.REGION
});

exports.uploadFile  = (filename, data, folderName) => {
return new Promise((resolve, reject) => {
    const params = {
        Bucket: process.env.AWS_S3_BUCKET, 
        Key: folderName+'/'+filename,
        Body: data.data,
        ACL:'public-read',
        ContentType: "image/jpeg"
    };
    s3.upload(params, function(s3Err, data) {
        if (s3Err) reject(s3Err)
        console.log(`File uploaded successfully at ${data.Location}`)
        resolve(`${data.Location}`)

    });
});
}

我现在使用邮递员上传文件作为表单数据。我可以正确看到使用此代码上传的文本文件。那么为什么图像有问题呢? 此外,图片和 pdf 的实际文件大小略有增加。

【问题讨论】:

  • data.data的数据类型是什么?
  • @ErmiyaEskandary request.files.FORM_DATA_NAME 是数据。
  • @ErmiyaEskandary 用更多细节更新了问题
  • 那么通过 Postman 上传的图片下载正确吗?仅在上传上述文件时出现问题还是根本无法正确下载图像?
  • @ErmiyaEskandary 正在上传。但下载后,我无法在图像查看器中查看它。此外,我可以看到上传后大小有所增加。也就是说,原来的文件大小是10kb,上传后会变成14kb。如果我下载它,大小将保持 14kb

标签: javascript amazon-web-services amazon-s3 serverless serverless-framework


【解决方案1】:

您需要将Body 作为BufferReadableStream 发送,因此在将其发送到s3 api 之前,您需要将request.files.image 作为Buffer 读取。还将内容类型添加到请求标头

我没有使用你的相同堆栈,但这是我的工作代码

CURL 测试命令

curl -X 'PUT' \
 'http://localhost/api' \
 -H 'accept: _/_' \
 -H 'Content-Type: multipart/form-data' \
 -F 'image=@/Users/some/dir/userprofilesample1.jpg;type=image/jpeg'

S3 api 调用 - 我没有指定 ContentType

.upload({
        Body: content, // Buffer | ReadableStream
        Bucket: bucketName,
        Key: path,
        ACL:  'public-read',
      })

https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html

【讨论】:

  • 能否请您指定邮递员的方式
  • 我不知道如何与邮递员一起做,为什么这很重要? curl 是更不容易出错的方法
  • 这就是为什么,因为我有点困惑如何处理图像数据以准备请求。
  • postman 添加了额外的标头,因此 curl 是您调试时的最佳选择。 curl sn-p 是一个工作示例,给它一个机会
  • 我通过 cUrl 尝试过。但没用
【解决方案2】:

我不确定问题出在哪里,但似乎无服务器脱机在处理作为 multipart/form-data 传入的数据时遇到了问题。最简单的解决方案是将文件编码为 base64 并将有效负载作为 application/json 发送。

要将文件编码为 base64,请使用此网站:https://base64.guru/converter/encode/file。在本地和以编程方式执行此操作很简单,但该网站是跨平台的,应该足以进行测试。

请求

有效载荷:

{

    "fileName": "sampleFile.jpg",
    "myFileData": "R0lGODlhPQBEAPeoAJosM//AwO/AwHVYZ/z595kzAP/s7P+goOXMv8+fhw/v739/f+8PD98fH/8mJl+fn/9ZWb8/PzWlwv///6wWGbImAPgTEMImIN9gUFCEm/gDALULDN8PAD6atYdCTX9gUNKlj8wZAKUsAOzZz+UMAOsJAP/Z2ccMDA8PD/95eX5NWvsJCOVNQPtfX/8zM8+QePLl38MGBr8JCP+zs9myn/8GBqwpAP/GxgwJCPny78lzYLgjAJ8vAP9fX/+MjMUcAN8zM/9wcM8ZGcATEL+QePdZWf/29uc/P9cmJu9MTDImIN+/r7+/vz8/P8VNQGNugV8AAF9fX8swMNgTAFlDOICAgPNSUnNWSMQ5MBAQEJE3QPIGAM9AQMqGcG9vb6MhJsEdGM8vLx8fH98AANIWAMuQeL8fABkTEPPQ0OM5OSYdGFl5jo+Pj/+pqcsTE78wMFNGQLYmID4dGPvd3UBAQJmTkP+8vH9QUK+vr8ZWSHpzcJMmILdwcLOGcHRQUHxwcK9PT9DQ0O/v70w5MLypoG8wKOuwsP/g4P/Q0IcwKEswKMl8aJ9fX2xjdOtGRs/Pz+Dg4GImIP8gIH0sKEAwKKmTiKZ8aB/f39Wsl+LFt8dgUE9PT5x5aHBwcP+AgP+WltdgYMyZfyywz78AAAAAAAD///8AAP9mZv///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAKgALAAAAAA9AEQAAAj/AFEJHEiwoMGDCBMqXMiwocAbBww4nEhxoYkUpzJGrMixogkfGUNqlNixJEIDB0SqHGmyJSojM1bKZOmyop0gM3Oe2liTISKMOoPy7GnwY9CjIYcSRYm0aVKSLmE6nfq05QycVLPuhDrxBlCtYJUqNAq2bNWEBj6ZXRuyxZyDRtqwnXvkhACDV+euTeJm1Ki7A73qNWtFiF+/gA95Gly2CJLDhwEHMOUAAuOpLYDEgBxZ4GRTlC1fDnpkM+fOqD6DDj1aZpITp0dtGCDhr+fVuCu3zlg49ijaokTZTo27uG7Gjn2P+hI8+PDPERoUB318bWbfAJ5sUNFcuGRTYUqV/3ogfXp1rWlMc6awJjiAAd2fm4ogXjz56aypOoIde4OE5u/F9x199dlXnnGiHZWEYbGpsAEA3QXYnHwEFliKAgswgJ8LPeiUXGwedCAKABACCN+EA1pYIIYaFlcDhytd51sGAJbo3onOpajiihlO92KHGaUXGwWjUBChjSPiWJuOO/LYIm4v1tXfE6J4gCSJEZ7YgRYUNrkji9P55sF/ogxw5ZkSqIDaZBV6aSGYq/lGZplndkckZ98xoICbTcIJGQAZcNmdmUc210hs35nCyJ58fgmIKX5RQGOZowxaZwYA+JaoKQwswGijBV4C6SiTUmpphMspJx9unX4KaimjDv9aaXOEBteBqmuuxgEHoLX6Kqx+yXqqBANsgCtit4FWQAEkrNbpq7HSOmtwag5w57GrmlJBASEU18ADjUYb3ADTinIttsgSB1oJFfA63bduimuqKB1keqwUhoCSK374wbujvOSu4QG6UvxBRydcpKsav++Ca6G8A6Pr1x2kVMyHwsVxUALDq/krnrhPSOzXG1lUTIoffqGR7Goi2MAxbv6O2kEG56I7CSlRsEFKFVyovDJoIRTg7sugNRDGqCJzJgcKE0ywc0ELm6KBCCJo8DIPFeCWNGcyqNFE06ToAfV0HBRgxsvLThHn1oddQMrXj5DyAQgjEHSAJMWZwS3HPxT/QMbabI/iBCliMLEJKX2EEkomBAUCxRi42VDADxyTYDVogV+wSChqmKxEKCDAYFDFj4OmwbY7bDGdBhtrnTQYOigeChUmc1K3QTnAUfEgGFgAWt88hKA6aCRIXhxnQ1yg3BCayK44EWdkUQcBByEQChFXfCB776aQsG0BIlQgQgE8qO26X1h8cEUep8ngRBnOy74E9QgRgEAC8SvOfQkh7FDBDmS43PmGoIiKUUEGkMEC/PJHgxw0xH74yx/3XnaYRJgMB8obxQW6kL9QYEJ0FIFgByfIL7/IQAlvQwEpnAC7DtLNJCKUoO/w45c44GwCXiAFB/OXAATQryUxdN4LfFiwgjCNYg+kYMIEFkCKDs6PKAIJouyGWMS1FSKJOMRB/BoIxYJIUXFUxNwoIkEKPAgCBZSQHQ1A2EWDfDEUVLyADj5AChSIQW6gu10bE/JG2VnCZGfo4R4d0sdQoBAHhPjhIB94v/wRoRKQWGRHgrhGSQJxCS+0pCZbEhAAOw==",
    "fileType": "image/jpeg",
    "productId": 12,
    "isDefault": false,
    "position": 2
}

product-image.js

const s3Functions = require('../services/s3Functions')
const { KEY_S3_PRODUCT_IMAGES_FOLDER } = require('../util/constants');
const { KEY_STATUS, KEY_DATA } = require('../util/constants');

exports.uploadProductImage = (body) => {
    return new Promise((resolve, reject) => {
        s3Functions.uploadFile(
            body.fileName,
            body.myFileData,
            body.fileType,
            KEY_S3_PRODUCT_IMAGES_FOLDER
        ).then(filename => {
            resolve({ [KEY_STATUS]: 1, [KEY_STATUS]: "Uploaded successfully", [KEY_DATA]: filename });
        }).catch(error => {
            reject({ [KEY_STATUS]: 0, [KEY_STATUS]: "Upload Failed", [KEY_DATA]: error });
        });
    })
}

s3Functions.js

const AWS = require('aws-sdk');

const s3 = new AWS.S3({
    params: { Bucket: 'rename-me', ACL:'public-read' },
});

exports.uploadFile = (
    filename,
    data,
    contentType,
    folderName
) => {
    console.log('Uploading')
    return new Promise((resolve, reject) => {
        const params = {
            Key: folderName+'/'+filename,
            Body: Buffer.from(data, 'base64'),
            ContentEncoding: 'base64',
            ContentType: contentType,
        };
        s3.upload(params, function(s3Err, data) {
            if (s3Err) {
                console.error(s3Err);
                reject(s3Err)
            }
            console.log(`File uploaded successfully at ${data.Location}`)
            resolve(`${data.Location}`)
        });
    });
}

Here's the codebase 您在另一个线程上提供了包含的更改。我还没有清理它,但以防万一你遇到任何问题。

【讨论】:

  • 如果我尝试上传超过 10MB 的图像文件,那么 myFileData 会很大,是否有机会中断上传?
  • @KIRANKJ 对,Base64 将数据大小增加了大约 30%。不理想,但它确实完成了工作。根据我的经验,处理表单是一件很痛苦的事情,除非您从实际的 HTML 表单提交。如果您的用户预计连接速度非常慢或文件大小很大(我的截断值大于 25MB),请不要使用 Base64。除此之外,确保 FE 中有适当的错误处理。
  • @KIRANKJ 我不明白你的第二个问题。您是在问上传是否可以中断?或者您是在问这是否会造成干扰?
  • 我的意思是因为表单数据量大,会不会崩溃?因为我刚刚上传了一个共享代码的 1MB 文件。上传花了8分钟。但上传后文件大小减小到 750KB。上传后图像的底部(近 25%)变灰。似乎这不是一个可靠的方法。我们是否正在尝试分段上传?如果没有,它会解决吗?还有,整个问题可以通过resign URL方式解决吗?
  • 嗯,我刚刚在 11.90 秒内通过共享的 10MBps 连接上传了一个 3MB 的文件。如果您正在处理本地(无服务器离线)实例,您是否可以尝试将其部署到实际的 lambda 以排除由您的机器引起的任何问题。还想提一下,我已经看到这种方法部署在生产系统中,所以我认为这不是不可靠的。当然,如果预签名 URL 方法适合您,那么这是没有实际意义的。这是一个不错的选择,但有几个缺点:增加的开发复杂性和多个请求。
【解决方案3】:

正如您问题的不同 cmet 中所指出的,有几件事可以激发您的问题。

请确保您提供了有关应被视为二进制的不同内容类型的必要配置。 AWS documentation 提供了有关它的详细信息;这个related SO question 也很有价值。

由于您使用的是serverless 框架,如您引用的链接1 2 中所示,请同时提供必要的配置:

provider:
  apiGateway:
    binaryMediaTypes:
      - 'multipart/form-data'

无论如何,似乎即使使用这种配置,您仍然面临问题。您告诉您能够成功上传文本文件,但您的图像已损坏,增加了它们的大小:如 cmets 所示,这似乎清楚地表明在某些地方信息正在转换为不同的编码,从二进制到文本,类似的东西。实际上,根据您的依赖关系,这似乎是实际问题,正如serverless-http 库的this issue 中所报告的那样,尤其是在serverless-offline 库的其他12 中。

我认为这个问题只是本地问题,它可能会在 AWS 中正​​常工作,而不会出现进一步的问题。

无论如何,正如您在上述第一个问题中看到的,与serverless-http 相关的问题,该库具有以下代码:

return Buffer.from(event.body, event.isBase64Encoded ? 'base64' : 'utf8'); 

因此,作为一种解决方法,以 base 64 编码提交信息可以解决问题:如果您在 HTML 中使用表单提交,这不是一项简单的任务 - 例如,请参阅great example 了解一些想法 - 尽管它如果您直接从代码中与您的 API 交互,则可以做到这一点。唯一必要的更改是在您的 params 变量中:

const params = {
  Bucket: process.env.AWS_S3_BUCKET, 
  Key: folderName+'/'+filename,
  Body: Buffer.from(data.data, 'base64'),
  ContentEncoding: 'base64',
  ContentType: 'image/jpeg',
  ACL:'public-read',
};

请注意Buffer.from(..., 'base64') 的使用和ContentEncoding: 'base64' 的包含。

无论如何,如果代码在 AWS 中有效,我认为要等待serverless-offline 问题解决。

【讨论】:

  • 如果我尝试上传超过 10MB 的图像文件,那么 myFileData 会很大,是否有机会中断上传?
  • @KIRANKJ s3.upload 将返回AWS.S3.ManagedUpload 的实例。您可以使用此对象提供的abort 方法取消正在进行的上传。你是这个意思吗?如果你的对象很大,比如说大于 100 Mb,你可以试试 S3 提供的multipart upload feature
  • 在转换为 base 64 时,我可以看到大小增加了近 30%。因此,辞职的 URL 可能是整个问题的替代方案。你有什么意见?我发现了更重要的一件事。我尝试了一个 1MB 文件的 base 64 上传。大约5分钟,还在上传。延迟可能是因为无服务器离线。但如果是这种情况,我无法使用正常的 1 到 2 MB 文件开发应用程序。那么,切换是个好主意吗?
  • 是的,base 64 编码肯定会增加您的文件大小。你的意思是使用createPresignedPost?像this 这样的东西?是的,当然这可能是一种选择。老实说,我从未测试过它。
  • @KIRANKJ 虽然最好的选择可能是使用预签名的 url,以防万一,考虑查看 this SO question,如果您正在处理图像,它可以是减小文件大小的选项也是。我希望它有所帮助。
猜你喜欢
  • 1970-01-01
  • 2012-01-08
  • 1970-01-01
  • 1970-01-01
  • 2016-06-19
  • 2015-09-06
  • 1970-01-01
  • 1970-01-01
  • 2015-12-02
相关资源
最近更新 更多