2017-07-03 25 views
3

POST interface経由でAWS S3にファイルをアップロードしたいのですが、失敗します。POSTでファイルをs3にアップロード

私はすでにPUTとgetSignedUrlで動作させていますが、残念ながらそのインターフェイスではファイルサイズの直接制限はできません。だから私は'content-length-range'の条件を使用できるので、私はPOSTインターフェイスを使用しようとしました。

はここに私の要求の署名です:

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); 
    }); 
}); 

この部分はOKのようですが、私はS3にファイルをアップロードするために必要な署名を使用することはできません。

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

別の試み:フェッチして

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

:ここ

は私が作ったいくつかの他の試みです

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) => {}); 

これらの作業のどちらも、彼らが悪い」と言うのいずれかリクエスト、または 'Badly Formed Request'と表示されます。そのうちの1人がサーバーに何かをアップロードしましたが、ファイルは読めませんでした。

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}`)); 
+0

S3 SDKは、サーバーがファイルを処理するために必要な余分なメタデータを送信すると考えています。 Fiddlerなどのプロキシを使用して、SDKから送信された要求を検査し、複製を開始することができます。 SDKリクエストと 'request.post'リクエストを比較し、その差を見つけて補正します。 これを行うことでさらに助けてもらえますが、JavaScriptの観点からはS3に精通していないので、.NET経由でしか使用していません。 最大バケットサイズについては、AWSのドキュメントを参照してください。 –

答えて

4

最後に動作します。誰かが同じ問題を抱えている場合のコードは次のとおりです。

いくつか注意すべき点:

  • 要求またはフォーム・データ・ライブラリにはバグがあり、そのうちの一つは、「コンテンツな長さ」ヘッダーを設定しません。問題を参照してくださいhttps://github.com/request/request/issues/316
  • フォームフィールドの順序は重要です、acl latel、それは失敗します。
  • そこにはさまざまな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); }); 
+0

作品!私は 'content-length'を持っておらず、' content-type'は 'multipart/form-data'です。また、AWS configに 'signatureVersion'と' region'を設定していません。 – wao813

関連する問題