我尝试使用Dropzone.js
将文件上传到S3服务我使用本教程直接从客户端上传文件:
https://devcenter.heroku.com/articles/s3-upload-node - 本教程并未包含dropzone js的实现(这是一场噩梦)
流程非常简单:
文件上传到服务器,直到这里一切正常,当我试图查看文件时(在S3 Bucket界面中),文件似乎没有正确写入,我不能这样做查看它。
根据源代码,使用FormData对象上传文件。
Dropzone.prototype.submitRequest = function(xhr, formData, files) {
return xhr.send(formData);
}
如果我更改源代码:
xhr.send(formData)
到
xhr.send(files[0])
一切都很好但我失去了上传多个文件的能力。
这是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
}
希望它足够:))
感谢。
答案 0 :(得分:8)
这是我在后端的dropzone init参数和节点S3签名上的作用:
使用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;
}
});
这是我在node.js端使用的库
var Crypto = require("crypto"),
AWS = require("aws-sdk"),
以下是S3上的CORS配置示例
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>PUT</AllowedMethod>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
以下是在node.js上生成S3签名的代码:
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();
}
});
}
希望其中一些有用。
答案 1 :(得分:3)
对于可能也会涉及此问题的人,我也想分享我的工作示例。 注意我通过脱掉自己的后端并使用AWS Lambda(又名无服务器)代替进行签名工作更进一步,但概念是相同的。
所以,基本上,
xhr.send
功能,如您所述。processFile
函数中的accept
,而不是依赖Dropzone的FormData来上传多个文件。因此,对于每个被接受的文件,上传将立即开始,并且您可以同时上传多个文件。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
})
上面的代码与Vue.js有关,但这个概念实际上是框架无关的,你明白了。有关完整的dropzone组件示例,请查看my GitHub repo。
答案 2 :(得分:1)
要上传到S3,必须处理两个单独的项目-身份验证和上传。
出于安全性考虑,有些可能性:
Aaron Rau演示了生成预签名链接。
使用STS在概念上更简单(无需对每个链接进行签名),但安全性稍差(相同的临时凭据可以在其他地方使用,直到它们过期)。
如果使用联合身份验证,则可以完全跳过服务器端!
从联合用户那里获取临时IAM凭据的一些很好的教程是here(对于FineUploader,但机制相同)和here。
要生成自己的临时IAM凭证,可以使用AWS-SDK。 PHP中的示例:
服务器:
<?php
require 'vendor/autoload.php';
use Aws\Result;
use Aws\Sts\StsClient;
$client = new StsClient(['region' => 'us-east-1', 'version' => 'latest']);
$result = $client->getSessionToken();
header('Content-type: application/json');
echo json_encode($result['Credentials']);
客户:
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);
要么直接使用DropZone并根据需要进行修改,要么让Dropzone成为aws-sdk的前端。
您需要包含
<script src="//sdk.amazonaws.com/js/aws-sdk-2.262.1.min.js"></script>
然后更新Dropzone以与其进行交互(基于this tutorial)。
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()
}
}
以上内容未经测试-仅添加了令牌,但不确定确实需要添加哪些标头。检查here,here和here中的文档,并可能使用FineUploader's implementation作为指南。
希望这会有所帮助,并且如果有人想添加对S3支持的拉取请求(如FineUploader中所示),我相信将不胜感激。