I'm trying to upload files to S3 service using Dropzone.js
我正在尝试使用Dropzone.js将文件上传到S3服务
I use this tutorial to upload the files directly from the client:
我使用本教程直接从客户端上传文件:
https://devcenter.heroku.com/articles/s3-upload-node - this tutorial doesn't include the implementation with dropzone js (which was a nightmare)
https://devcenter.heroku.com/articles/s3-upload-node -本教程不包括dropzone js的实现(这是一场噩梦)
The flow is pretty simple:
流程非常简单:
The file is uploaded to the server, until here everything is ok, when I'm trying to view the file (in S3 Bucket interface) it seems like the file was not write correctly and i can't view it.
文件被上传到服务器上,直到这里一切正常,当我试图查看文件(在S3 Bucket接口中)时,似乎文件没有写对,我无法查看它。
According to the source code the file is upload using FormData object.
根据源代码,文件是使用FormData对象上载的。
Dropzone.prototype.submitRequest = function(xhr, formData, files) {
return xhr.send(formData);
}
if i change the source code from:
如果我将源代码从:
xhr.send(formData)
to
来
xhr.send(files[0])
Everything works great but i lose to ability to upload multiple files.
一切都很好,但我无法上传多个文件。
This is the dropzone config:
这是dropzone的配置:
{
url: 'http://signature_url',
accept: _dropzoneAcceptCallback,
method: 'put',
headers: {
'x-amz-acl': 'public-read',
'Accept': '*/*',
'Content-Type': file.type
},
clickable: ['.choose-files'],
autoProcessQueue: false
}
Hope it's enough :)
希望就足够了:)
Thanks.
谢谢。
3
For someone who might also jumped into this question, I'd like to share my working example as well. Note that I went a step further by taking off my own backend and use AWS Lambda (aka. serverless) instead to do the signing job, the concept is the same though.
对于可能也会进入这个问题的人,我也想分享我的工作例子。注意,我更进一步地去掉了自己的后端并使用AWS Lambda(又名AWS Lambda)。相反,要做签名工作,概念是一样的。
So, basically,
所以,基本上,
xhr.send
function as you already mentioned.processFile
inside the accept
function. So the upload will starts immediately for each file being accepted and you're able to upload multiple files simultaneously.const vm = this
let optiOns= {
// The URL will be changed for each new file being processing
url: '/',
// Since we're going to do a `PUT` upload to S3 directly
method: 'put',
// Hijack the xhr.send since Dropzone always upload file by using formData
// ref: https://github.com/danialfarid/ng-file-upload/issues/743
sending (file, xhr) {
let _send = xhr.send
xhr.send = () => {
_send.call(xhr, file)
}
},
// Upload one file at a time since we're using the S3 pre-signed URL scenario
parallelUploads: 1,
uploadMultiple: false,
// Content-Type should be included, otherwise you'll get a signature
// mismatch error from S3. We're going to update this for each file.
header: '',
// We're going to process each file manually (see `accept` below)
autoProcessQueue: false,
// Here we request a signed upload URL when a file being accepted
accept (file, done) {
lambda.getSignedURL(file)
.then((url) => {
file.uploadURL = url
done()
// Manually process each file
setTimeout(() => vm.dropzone.processFile(file))
})
.catch((err) => {
done('Failed to get an S3 signed upload URL', err)
})
}
}
// Instantiate Dropzone
this.dropzOne= new Dropzone(this.$el, options)
// Set signed upload URL for each file
vm.dropzone.on('processing', (file) => {
vm.dropzone.options.url = file.uploadURL
})
The code above has something related to Vue.js, but the concept is actually framework agnostic, you get the idea. For a full working dropzone component example, please have a look at my GitHub repo.
上面的代码与Vue相关。但是这个概念实际上是框架不可知的,你懂的。对于一个完整的工作dropzone组件示例,请查看我的GitHub repo。
8
Here's what worked for my on the dropzone init parameters and node S3 signature on the backend:
下面是我在后台的dropzone init参数和节点S3签名的工作原理:
HTML Frontend Code using Dropzone:
使用Dropzone的HTML前端代码:
var myDropzOne= new Dropzone(dropArea, {
url:"#",
dictDefaultMessage: "Drag n drop or tap here",
method: "PUT",
uploadMultiple: false,
paramName: "file",
maxFiles: 10,
thumbnailWidth: 80,
thumbnailHeight: 80,
parallelUploads: 20,
autoProcessQueue: true,
previewTemplate: dropPreviewTemplate,
//autoQueue: false, // Make sure the files aren't queued until manually added
previewsContainer: dropPreviewContainer, // Define the container to display the previews
clickable: true, //".fileinput-button" // Define the element that should be used as click trigger to select files.
accept: function(file, cb) {
//override the file name, to use the s3 signature
//console.log(file);
var params = {
fileName: file.name,
fileType: file.type,
};
//path to S3 signature
$.getJSON('/uploader', params).done(function(data) {
//console.log(data);
if (!data.signedRequest) {
return cb('Failed to receive an upload url');
}
file.signedRequest = data.signedRequest;
file.finalURL = data.downloadURL;
cb();
}).fail(function() {
return cb('Failed to receive an upload url');
});
},
sending: function(file, xhr) {
console.log('sending')
var _send = xhr.send;
xhr.setRequestHeader('x-amz-acl', 'public-read');
xhr.send = function() {
_send.call(xhr, file);
}
},
processing:function(file){
this.options.url = file.signedRequest;
}
});
Here's the libraries I used on the node.js side
这是我在节点上使用的库。js的一面
var Crypto = require("crypto"),
AWS = require("aws-sdk"),
Here's a sample of the CORS config on S3
这是S3中CORS配置的一个示例。
*
PUT
*
Here's the code to generate the S3 Signature on node.js :
下面是在节点上生成S3签名的代码。js:
getPolicy:function(req,res)
{
var fileId = Crypto.randomBytes(20).toString('hex').toUpperCase();
var prefix = "bl_";
var newFileName = prefix+fileId;//req.query.fileName;
var s3 = new AWS.S3();
var s3_params = {
Bucket: BUCKET,
Key: newFileName,
Expires: 60,
ContentType: req.query.fileType,
ACL: 'public-read'
};
s3.getSignedUrl('putObject', s3_params, function(err, data){
if(err){
console.log(err);
}
else{
var return_data = {
signedRequest: data,
uploadURL: 'https://'+BUCKET+'.s3.amazonaws.com/'+newFileName,
downloadURL: 'http://'+BUCKET+'.s3-website-us-east-1.amazonaws.com/'+newFileName,
};
res.write(JSON.stringify(return_data));
res.end();
}
});
}
Hopefully some of this is helpful.
希望这能有所帮助。
0
There are two separate items that must be dealt with to upload to S3 - authentication and uploading.
要上载到S3,必须处理两个单独的项目——身份验证和上载。
AuthSome possibilities, in order of security:
一些可能性,为了安全:
Generating pre-signed links was demonstrated by Aaron Rau.
Aaron Rau演示了生成预签名链接。
Using STS is conceptually simpler (no need to sign each link), but is somewhat less secure (the same temp credentials can be used elsewhere until they expire).
使用STS在概念上更简单(不需要对每个链接进行签名),但是有点不安全(相同的临时凭证可以在其他地方使用,直到过期)。
If you use federated auth, you can skip the server-side entirely!
Some good tutorials for getting temporary IAM credentials from federated users, are here (for FineUploader, but the mechanism is the same)] and here.
如果您使用联邦授权,您可以完全跳过服务器端!从联邦用户那里获得临时IAM凭据的一些很好的教程在这里(对于FineUploader,但是机制是相同的)和这里。
To generate your own temporary IAM credentials you can use the AWS-SDK. An example in PHP:
要生成自己的临时IAM凭据,可以使用AWS-SDK。PHP中的一个例子:
Server:
服务器:
'us-east-1', 'version' => 'latest']);
$result = $client->getSessionToken();
header('Content-type: application/json');
echo json_encode($result['Credentials']);
Client:
客户:
let dropzOnesetup= async () => {
let creds = await fetch('//example.com/auth.php')
.catch(console.error);
// If using aws-sdk.js
AWS.config.credentials = new AWS.Credentials(creds);
Uploading
Either use DropZone natively and amend as needed, or have Dropzone be a front for the aws-sdk.
或者直接使用DropZone并根据需要进行修改,或者让DropZone成为aws-sdk的前端。
You need to include it
你需要包含它
And then update Dropzone to interact with it (based on this tutorial).
然后更新Dropzone以与之交互(基于本教程)。
let canceled = file => { if (file.s3upload) file.s3upload.abort() }
let optiOns=
{ canceled
, removedfile: canceled
, accept (file, done) {
let params = {Bucket: 'mybucket', Key: file.name, Body: file };
file.s3upload = new AWS.S3.ManagedUpload({params});
done();
}
}
// let aws-sdk send events to dropzone.
function sendEvents(file) {
let progress = i => dz.emit('uploadprogress', file, i.loaded * 100 / i.total, i.loaded);
file.s3upload.on('httpUploadProgress', progress);
file.s3upload.send(err => err ? dz.emit('error', file, err) : dz.emit('complete', file));
}
Dropzone.prototype.uploadFiles = files => files.map(sendEvents);
var dz = new Dropzone('#dz', options)
let optiOns=
{ method: 'put'
// Have DZ send raw data instead of formData
, sending (file, xhr) {
let _send = xhr.send
xhr.send = () => _send.call(xhr, file)
}
// For STS, if creds is the result of getSessionToken / getFederatedToken
, headers: { 'x-amz-security-token': creds.SessionToken }
// Or, if you are using signed URLs (see other answers)
processing: function(file){ this.options.url = file.signedRequest; }
async accept (file, done) {
let url = await fetch('https://example.com/auth.php')
.catch(err => done('Failed to get an S3 signed upload URL', err));
file.uploadURL = url
done()
}
}
The above is without testing - have added just the token, but am not sure which headers really needed to be added. Check here, here and here for the docs, and perhaps use FineUploader's implementation as a guide.
上面没有测试——只添加了令牌,但不确定真正需要添加哪些头。在这里、这里和这里检查文档,或者使用FineUploader的实现作为指南。
Hopefully this will help, and if anyone wants to add a pull request for S3 support (as is in FineUploader), I'm sure it will be appreciated.
希望这能有所帮助,如果有人想添加对S3支持的拉取请求(正如FineUploader中所做的那样),我肯定会很感激。