使用POST将文件上传到s3

Ben*_*dai 11 amazon-s3 node.js aws-sdk

我想通过POST接口将文件上传到AWS S3 ,但我没有这样做。

我已经使它可以与PUT和一起使用getSignedUrl,但是不幸的是,该接口不允许直接文件大小的限制。因此,我尝试使用POST接口,因为在那里可以使用'content-length-range'condition。

这是我的请求签名:

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

aws.config.update({
    signatureVersion: 'v4',
    region: 'eu-central-1',
    accessKeyId: config.aws.keyId,
    secretAccessKey: config.aws.keySecret
});

const s3 = new aws.S3();

return new Promise((resolve, reject) => {
    const params = {
        Bucket: config.aws.bucket,
        Fields: {
            key: filePath
        },
        Expires: config.aws.expire,
        Conditions: [
            ['acl', 'public-read'],
            ['content-length-range', 0, 10000000] // 10 Mb
        ]
    };
    const postUrl = s3.createPresignedPost(params, (err, data) => {
        resolve(data);
    });
});
Run Code Online (Sandbox Code Playgroud)

这部分似乎还可以,但是我不能使用所需的签名将文件上传到S3。

这是我所做的其他尝试:

request.post({
    url: payload.url,
    body: payload,
    form: fs.createReadStream(__dirname + `/${filePath}`)
}, (err, response, body) => {});
Run Code Online (Sandbox Code Playgroud)

另一尝试:

let formData = payload;
formData.file = fs.createReadStream(__dirname + `/${filePath}`);
request.post({ 
    url: payload.url,
    formData: formData
}, (err, response, body) => {});
Run Code Online (Sandbox Code Playgroud)

提取时:

const fetch = require('node-fetch');
const FormData = require('form-data');

const form = new FormData();
const fields = payload.fields;
for(const field in payload.fields) {
    form.append(field, payload.fields[field]);
}
form.append('file', fs.createReadStream(__dirname + `/${filePath}`));
fetch(payload.url, {
    method: 'POST',
    body: form.toString(),
    headers: form.getHeaders()
})
.then((response) => {})
.catch((err) => {});
Run Code Online (Sandbox Code Playgroud)

这些都不起作用,它们要么说“错误请求”,要么说“格式错误的请求”。其中一个将某些内容上传到服务器,但是该文件不可读。

如何为S3存储桶添加最大文件大小限制?

更新: 我想我前进了一点。有了这段代码,我得到了错误响应:You must provide the Content-Length HTTP header.

const fetch = require('node-fetch');
const FormData = require('form-data');

const form = new FormData();
form.append('acl', 'public-read');
for(const field in payload.fields) {
    form.append(field, payload.fields[field]);
}
form.append('file', fs.createReadStream(__dirname + `/${filePath}`));

fetch(payload.url, {
    method: 'POST',
    body: form,
    headers: form.getHeaders()
})
.then((response) => { return response.text(); })
.then((payload) => { console.log(payload); })
.catch((err) => console.log(`Error: ${err}`));
Run Code Online (Sandbox Code Playgroud)

Ben*_*dai 11

终于可以了。如果有人遇到同样的问题,这里是代码。

注意事项:

  • 请求或表单数据库有一个错误,其中一个没有设置“ Content-Lenght”标头。参见问题https://github.com/request/request/issues/316
  • 表单字段的顺序很重要,acl但是,它将失败。
  • 那里有不同的AWS协议,您应该检查区域中可用的协议。就我而言,我必须在S3构造函数中设置signatureVersionV4甚至。

我不为代码质量感到骄傲,但最终它可以工作。

const aws = require('aws-sdk');
const fs = require('fs');
const request = require('request');
const config = require('./config');

let s3;

const init = () => {
    aws.config.update({
        signatureVersion: 'v4',
        region: 'eu-central-1',
        accessKeyId: config.aws.keyId,
        secretAccessKey: config.aws.keySecret
    });

    s3 = new aws.S3({signatureVersion: 'v4'});
};

const signFile = (filePath) => {
    return new Promise((resolve, reject) => {
        const params = {
            Bucket: config.aws.bucket,
            Fields: {
                key: filePath
            },
            Expires: config.aws.expire,
            Conditions: [
                ['content-length-range', 0, 10000000], // 10 Mb
                {'acl': 'public-read'}
            ]
        };
        s3.createPresignedPost(params, (err, data) => {
            resolve(data);
        });
    });
};

const sendFile = (filePath, payload) => {
    const fetch = require('node-fetch');
    const FormData = require('form-data');

    const form = new FormData();
    form.append('acl', 'public-read');
    for(const field in payload.fields) {
        form.append(field, payload.fields[field]);
    }
    form.append('file', fs.createReadStream(__dirname + `/${filePath}`));
    form.getLength((err, length) => {
        console.log(`Length: ${length}`);
        fetch(payload.url, {
            method: 'POST',
            body: form,
            headers: {
                'Content-Type': false,
                'Content-Length': length
            }
        })
        .then((response) => {
            console.log(response.ok);
            console.log(response.status);
            console.log(response.statusText);
            return response.text();
        })
        .then((payload) => {
            console.log(payload);
            console.log(form.getHeaders());
        })
        .catch((err) => console.log(`Error: ${err}`));
    });

};


init();

const file = 'test.pdf';
const filePath = `files/new/${file}`;
signFile(filePath)
.then((payload) => { sendFile(file, payload); });
Run Code Online (Sandbox Code Playgroud)

  • 我也一直在与该上传进行斗争。这几乎可行。我必须删除“ Content-Type”行:false,以使其正常工作。否则,我会收到一个错误消息`<Code> PreconditionFailed </ Code> <Message>至少您指定的一个前提条件不成立</ Message> <Condition>存储桶POST必须为存储柜类型multipart / form -data </ Condition>` (3认同)