如何根据预先签名的URL写入S3存储桶中的现有文件?

时间:2019-07-01 17:07:43

标签: node.js amazon-web-services url amazon-s3 aws-lambda

我一直在寻找一种从预签名URL写入S3存储桶中JSON文件的方法。从我的研究看来,这是可以做到的,但是这些不在Node中:

没有从我的搜索中找到节点解决方案,而是使用第三方API,我试图将回调写到S3存储桶中的JSON。我可以毫无问题地生成预签名URL,但是当我尝试将虚拟文本写入预签名URL时,我得到:

  

错误:ENOENT:没有这样的文件或目录,请打开   'https://path-to-file-with-signed-url'

当我尝试使用writeFile时:

fs.writeFile(testURL, `This is a write test: ${Date.now()}`, function(err) {
  if(err) return err
  console.log("File written to")
})

根据我对file下的documentation的理解,它说我可以使用URL。我开始相信这可能是权限问题,但是我在文档中找不到任何运气。

实现node-fetch后,仍然会基于预签名URL向S3中的文件写入错误(403 Forbidden),这是我编写的模块中的完整代码:

const aws = require('aws-sdk')
const config = require('../config.json')
const fetch = require('node-fetch')
const expireStamp = 604800 // 7 days

const existsModule = require('./existsModule')

module.exports = async function(toSignFile) {
  let checkJSON = await existsModule(`${toSignFile}.json`)
  if (checkJSON == true) {
    let testURL = await s3signing(`${toSignFile}.json`)
    fetch(testURL, {
      method: 'PUT',
      body: JSON.stringify(`This is a write test: ${Date.now()}`),
    }).then((res) => {
      console.log(res)
    }).catch((err) => {
      console.log(`Fetch issue: ${err}`)
    })
  }
}

async function s3signing(signFile) {
  const s3 = new aws.S3()
  aws.config.update({
    accessKeyId: config.aws.accessKey,
    secretAccessKey: config.aws.secretKey,
    region: config.aws.region,
  })
  params = {
    Bucket: config.aws.bucket,
    Key: signFile,
    Expires: expireStamp
  }
  try {
    // let signedURL = await s3.getSignedUrl('getObject', params)
    let signedURL = await s3.getSignedUrl('putObject', params)
    console.log('\x1b[36m%s\x1b[0m', `Signed URL: ${signedURL}`)
    return signedURL
  } catch (err) {
    return err
  }
}

查看权限我没有关于上传的问题,并且在权限中设置了写访问权限。在Node中,如何使用该文件的预签名URL作为路径写入S3存储桶中的文件?

2 个答案:

答案 0 :(得分:1)

fs文件系统模块。您不能将其用作HTTP客户端。

您可以使用内置的https模块,但我想您会发现使用node-fetch会更容易。

fetch('your signed URL here', {
  method: 'PUT',
  body: JSON.stringify(data),
  // more options and request headers and such here
}).then((res) => {
  // do something
}).catch((e) => {
  // do something else
});

答案 1 :(得分:0)

正在寻找一种优雅的方法来使用PUT将s3文件传输到s3签名的url。我发现的大多数示例都使用PUT({body:data})。我遇到了一个建议,将数据读取到可读流中,然后将其通过管道传输到PUT。但是,我仍然不喜欢将大文件加载到内存中,然后将其分配给put流的想法。用管道读和写总是在内存和性能上更好。由于s3.getObject()。createReadStream()返回支持管道的请求对象,因此我们要做的就是将其正确地管道传递到公开写入流的PUT请求。

获取对象函数

async function GetFileReadStream(key){  
    return new Promise(async (resolve,reject)=>{  
        var params = {
            Bucket: bucket, 
            Key: key
            };
        var fileSize = await  s3.headObject(params)
        .promise()
        .then(res => res.ContentLength);
        resolve( {stream : s3.getObject(params).createReadStream(),fileSize});
    });
}

放置对象功能

const request = require('request');
async function putStream(presignedUrl,readStream){
    return new Promise((resolve,reject)=>{
        var putRequestWriteStream =  request.put({url:presignedUrl,headers:{'Content-Type':'application/octet-stream','Content-Length':readStream.fileSize  }});
        putRequestWriteStream.on('response', function(response) {           
            var etag = response.headers['etag'];
            resolve(etag); 
            })
            .on('end', () => 
                console.log("put done"))
        readStream.stream.pipe(putRequestWriteStream);
    });
}

这非常适合占用很小的内存空间。享受。