首页 » SEO优化 » phpplupload分片上传技巧_Node开拓文件上传系统及向七牛云存储和亚马逊AWS S3的文件上传

phpplupload分片上传技巧_Node开拓文件上传系统及向七牛云存储和亚马逊AWS S3的文件上传

访客 2024-12-16 0

扫一扫用手机浏览

文章目录 [+]

有伟人曰:学习技能的最好路子莫过于理论与实践相结合。

初学Node这货时,每每读教程必会Fall asleep。

phpplupload分片上传技巧_Node开拓文件上传系统及向七牛云存储和亚马逊AWS S3的文件上传

当真要开拓系统时,顿觉精神百倍,即便踩坑无数也不失落斗志。

phpplupload分片上传技巧_Node开拓文件上传系统及向七牛云存储和亚马逊AWS S3的文件上传
(图片来自网络侵删)

由于同团队的小伙伴们都在费力事情,正是由于他们的事情,

才让我有足够的韶光拖着我疲软的智商来研究Node和AWS这些货。

系统完成,虽不尽完善,但不敢怠慢,迅速记录,免遗忘。

为后续更新和开拓做一参考。

这便是人生。
只要努力,便美美哒。

标题略长,实在这系统要做的事只三件:

1. 从本地上传文件到我们自己的做事器,并存储。

2. 将文件上传到七牛云存储。

3. 将文件上传到亚马逊的AWS S3存储。

几处解释:

1. 用Node的好处是写做事端代码也不用纠结语法问题了:

系统的开拓用Node完成。
写前后端都是JS,免去了语法的困扰。

不仅回顾起数日之前写Scala时对语法的纠结和困惑,一身冷汗。

2. Plupload是个好东东:

Client真个File Select用Plupload完成。

有了Plupload这货,再不纠结<input type='file'>的丢脸样式的兼容问题不好把控了。

Plupload虽然对File做了封装,但也供应了如 getNative等的接口供我们访问原生。

十分谅解。

3. AWS的Upload在前端完成:

原形只有一个:在Node做事真个AWS的Upload我还没跑通……

请尽情的鄙视我吧T_T

好在路路通罗马。
我绕路从前端赶到了罗马。

做事端要求的Block在这里:

从做事端向AWS上传文件时,其文件的Body以流办法被分块上传。

测试后创造,上传完成,也只传了部分,导致文件无法正常访问。

而在前端上传时,直接用原生File工具即可实现上传。

遂成功抵达罗马。

关于在做事真个上传问题,有待连续研究。

学海无涯0_0

4. 七牛的上传在做事端完成:

七牛的上传也可以在前端完成,只不过七牛自己的JS-SDK包裹了Plupload。

由于我的上传逻辑是由自己的Plupload来触发七牛和亚马逊(或其他第三方上传),

因此不在前端再New一个Plupload来做七牛的上传了。

New两个同样的东西实在是太二了好么。

设计的理念是,所有第三方上传都必须在我们的做事器Trigger之后才发生。

就酱任性。

—————— 我是冬季里颤巍巍的分割线 ——————

紧张逻辑和部分代码:

1. 主程序和框架:

利用Express框架和Jade渲染引擎。

主程序app.js只做做事器的创建和监听,

涉及业务逻辑的要乞降处理,都写在二级目录(./routes)的模块里。

app.js 的部分内容如下:

3 var express = require('express'); 4 var favicon = require('serve-favicon'); 5 var bodyParser = require('body-parser'); 6 var debug = require('debug')('express:server'); 7 var http = require('http'); 8 var port = normalizePort(process.env.PORT || '3038'); 9 var app = express; 10 var server = http.createServer(app); 11 var index =require('./routes/index'); // 业务逻辑在这里 12 13 app.set('port', port); 14 server.on('error', serverOnError); 15 server.on('listening', serverOnListening); 16 server.on('connection', serverOnConnecting); 17 server.listen(port); 18 19 app.set('views', path.join(__dirname, 'views')); 20 app.set('view engine', 'jade'); 21 app.use(favicon(path.join(__dirname, 'public/lib', 'favicon.ico'))); 22 app.use(bodyParser.json); 23 app.use(bodyParser.urlencoded({ extended: true })); 24 app.use(express.static(path.join(__dirname, 'public'))); 25 26 app.use('/', index);

1 / ====================================================== /

2 module.exports = app;

2. POST要求将文件上传并存储在本地做事器:

须要把稳的是,这里的POST要求用到了中间件:

1 var multipart = require('connect-multiparty');\r3 var multipartMiddleware = multipart;\r5 var express = require('express');\r7 var router = express.Router;\r9 router.post( ‘/saveInLocalServer’, multipartMiddleware, function(req, res){ 。


});

这个要求吸收的是从前真个Plupload上传的File,

神秘的中间件会在做事器天生临时文件,但不会删除它们。

因此在处理的末了要手动删除临时文件req.files。
How to?

收到要求后,处理文件的部分代码如下:

1 var file = req.files.file;\r 2 var tempPath = file.path,\r 3 fileName = file.name,\r 4 fileType = file.type,\r 5 fileSize = file.size;\r 6 var uploadDirName = dirName.DirName; // 天生目录的模块,每月生平\r 7 var filenameWithMd5 = MD5( new Date.getTime ) + '-' + fileName;\r 8 var filenameForCloud = fileRename.FileRename(fileName);\r 9 // 保存到本地做事器的文件,利用MD5重命名文件\r10 // 上传到云存储的文件,利用自定义的模块重命名\r11 var targetPath = path.resolve('./' + uploadDirName + '/' + filenameWithMd5);\r12 // Save file in our local server:\r13 fs.rename(tempPath, targetPath, function(err, data){\r14 if( err ){\r15 var result = 'error';\r16 res.status( result ).send;\r17 } else {\r18 var result = 'ok';\r19 var uploadInfos = { ... }; // AWS的config信息定义在做事端,由模块引入并发送到前端,供JS接口调用:\r20 res.status( result ).send( uploadInfos );\r21 // Next do Qi Niu Upload ... blah blah blah\r22 }\r23 });

针对上述代码的几处解释:

a:关于在本地做事器天生目录:

我们的需求是,每月首次触发上传动作时,在做事器创建一只新目录。

该月内的别的上传文件,都存储在这一目录里。

所有的文件会按上传韶光,以自然月为目录而分类。

按月创建目录的逻辑,我写了一枚小小模块,如下:

var fs = require('fs');\r\rvar _d = new Date;\rvar _year = _d.getFullYear;\rvar _month = (_d.getMonth + 1 < 10)?('0' + (_d.getMonth + 1)):(_d.getMonth + 1); // 为整洁,月份都显示为两位数,因此1-9月前面加0\rvar dir = _year + '-' + _month + '-alex_upload';\r\rif (!fs.existsSync(dir)){\r fs.mkdirSync(dir);\r}\r\rexports.DirName = dir; // 输出模块名为DirName\r\r// ============================\r// 假设这个文件名为makeDirName.js,则在业务逻辑中引入并运用要这样:\rvar d_name = require('../routes/makeDirName');\rvar someName = d_name.DirName; // 输出的模块名在这里被这样引用

b:关于文件重命名:

我们的需求是,存在本地做事器的文件,利用MD5重命名。

上传到云存储的文件,利用韶光戳和随机字符串共同重命名。

重命名文件的模块是酱紫写的:

1 function rename( filename ) { \r 2 var name = ''; \r 3 var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\r 4 var length = 6; // 随机字符串的长度,暂用6\r 5 for(var i = 0; i < length; i++){\r 6 name += possible.charAt( Math.floor(Math.random possible.length) );\r 7 }\r 8 var timestamp = new Date.getTime;\r 9 name = timestamp + '-' + name + '-' + filename;\r10 return name;\r11 };\r12 \r13 exports.FileRename = rename;

c:关于res.status( 200 ).send( data ):

每个要求的response必须Call一下res.end,

以此来见告做事器这个要求的header和body都已发送,

并且这个要求已经完成。

如果不见告做事器,呆萌的做事器是永久不会知道的。

浏览器会一贯在要求状态中,标题栏的小圈圈一贯在转啊转,

表示要求一贯在持续啊持续。

在Call了res.end之后,res.finished 的值为true,否则是false。

res.send 会Call res.end,因此不需重复Call。

3:在前端要求AWS S3

在发送刚才所提到的POST要求之前,

前端先new一个plupload的Uploader,部分代码如下:

1 var _myUploader = new plupload.Uploader({\r 2 runtimes: 'html5,flash,silverlight,html4',\r 3 file_data_name: 'file',\r 4 container: _SCOPE.containerId,\r 5 browse_button: _SCOPE.filePickerId,\r 6 uptoken_url: _ELE.fileUptoken.innerHTML,\r 7 url: _ELE.fileLocalSave.innerHTML,\r 8 flash_swf_url: _SCOPE.swfUrl,\r 9 silverlight_xap_url:_SCOPE.xapUrl,\r10 filters: {\r11 max_file_size: _SCOPE.maxFileSize,\r12 mime_types: [\r13 {title: 'Image files', extensions: 'jpg,png,gif'},\r14 {title: 'Zip files', extensions: 'zip'}\r15 ] \r16 }, \r17 init: {...}\r18 });

这里的 _SCOPE 和 _ELE 定义在全局浸染域,或指定页面模块浸染域下。

目的是从做事端吸收干系的配置参数,在页面发送要求时调用。

这里遵照了一个高端大气上档次的写码原则,即:

常量参数的配置,

如Domain地址、取token之通信接口、

even 账户的accessKey&accessToken blah blah blah……

都在做事端某指定模块内统一配置。

当前端须要某参数时,由页面渲染res.render 通报到页面元素HTML属性里,

但是不可以将Key等账户密钥渲染在页面构造里。

也可以通过前后端通信将参数通报给前端页面,

例如刚才所述的POST接口里的uploadInfos。

这样做,在一处定义,别的皆调用。

当值有更新时,只在定义处更新其值即可。

避免多处赋值,更新时丢三落四陷入混乱。

嗯咳,所有工程师都知道的好么!
我说多了……

……连续说上传:

利用Plupload,在其FileUploaded 的回调里,

即可实行向AWS S3发送要求了。

FileUploaded是在Plupload的文件上传成功后才会触发。

前端要求AWS S3的简要方法如下:

(这里的file是从FileUploaded的方法里用getNative获取到的原生file工具)

1 function doAWSUpload( rename, file, info ) { 2 var file_name = file.name, 3 file_type = file.type, 4 file_size = file.size; 5 var bucket = new AWS.S3; 6 var uniqueName = rename; 7 bucket.config.update({ // 配置信息,在做事端传来的info里 8 accessKeyId: info.accessKeyId, 9 secretAccessKey: info.secretAccessKey 10 }); 11 bucket.config.region = info.region; 12 var params = { 13 Bucket: info.bucket, // 账户指定的bucket名 14 Key: uniqueName, 15 ContentType: file_type, 16 Body:file,ACL: 'public-read', // 设置文件访问权限 17 ServerSideEncryption: info.ServerSideEncryption 18 }; 19 bucket.putObject(params, function(err, data){ // 此账户必须要有putObject的操作权限才能调用 20 if(err){ 21 var errText = ' ' + file_name + ' failed in uploading to AWS! ' + err; 22 _ELE.fileConsole.innerHTML += errText; 23 }else{ 24 var url = 'https://s3.amazonaws.com/' + info.bucket + '/' + uniqueName;26 _ELE.fileConsole.innerHTML += ' AWS upload succeeded! ' + url; 27 } 28 }).on('httpUploadProgress', function(progress){ 29 console.log( 'AWS uploading...', Math.round(progress.loaded / progress.total 100) ); 30 }); 31 };

实行这个方法的条件是前端页面调用了JS-SDK,

并且,……最主要的是并且:

对应账户在AWS的Console管理后台的干系配置要精确。

最讨厌各种干系配置了,

配来配去一百年才成功一次……

4:AWS的账户在Console管理后台的干系配置

首先注册一枚高大上的AWS账户。

如果你常常在Amazon上买买买,也可以用你的Retail账户。

开通AWS做事,须要验证,其过程要填写Payment账户信息。

我十分Naive的填了自己的Credit Card信息,结果直接被扣掉1刀勒。

吓尿之后,急速删。

大约由于作为Retail账户时我曾做过快捷支付神马的脑残设置吧。

总之,1美元而已,这已不是重点……

有了一枚洒脱的AWS账户后,登录 https://console.aws.amazon.com

选择S3做事,进来后忽略统统,先Create Bucket,

点击这个新的Bucket,选择Properties,

在Permissions里,再选择 “Edit CORS Configuration”,

一个较为范例的CORS Configuration可以长这个样子:

1 <?xml version=\"大众1.0\"大众 encoding=\公众UTF-8\公众?>\r 2 <CORSConfiguration xmlns=\"大众http://s3.amazonaws.com/doc/2006-03-01/\"大众>\r 3 <CORSRule>\r 4 <AllowedOrigin>http://localhost:3038</AllowedOrigin> //本地测试入口\r 5 <AllowedOrigin>http://shaojing.wang</AllowedOrigin> //线上测试入口\r 6 <AllowedMethod>PUT</AllowedMethod> //可实行的方法\r 7 <AllowedMethod>DELETE</AllowedMethod> //可实行的方法\r 8 <MaxAgeSeconds>3000</MaxAgeSeconds>\r 9 <ExposeHeader>x-amz-server-side-encryption</ExposeHeader>\r10 <ExposeHeader>x-amz-request-id</ExposeHeader>\r11 <ExposeHeader>x-amz-id-2</ExposeHeader>\r12 <AllowedHeader></AllowedHeader>\r13 </CORSRule>\r14 </CORSConfiguration>

这里的CORS Configuration即对跨域要求所做限定,

只有“AllowedOrigin”里指定的端口才能向AWS发出要求,

而只有“AllowedHeader”里指定的端口才能吸收要求(访问文件)。

上传成功后,可通过这样的URI访问到文件:

https://s3.amazonaws.com/myBucketName/1452581386878-hPp8Mc-test.png

附:AWS的文档在这里:http://docs.aws.amazon.com/

关于如何在做事端进行AWS S3的上传,下次再写文章分享。

下面该讲什么了……

5:在做事端实现向七牛云存储上传文件

该七牛了。

请八牛、九牛和十牛再耐心等一等。

六牛你不要闹,你已经谢世了好么。

从做事器向七牛云发送要求之前,须要获取授权,

要求授权之前,须要设置账户信息。

设置账户信息之前,你得先有一枚账户。

有了账户就有了AccessKey & SecretKey。

还是刚才讲的,在统一配置参数的模块里,配置好这些Key们的信息,

然后在做事端将发送要求之前,做赋值:

1 var qiniu = require('qiniu');\r2 var qnConf = require('../config/qiniu_config');\r3 \r4 / Prepare Qiniu config, we make Qiniu upload in Node Server not in browser/\r5 qiniu.conf.ACCESS_KEY = qnConf.QiniuConfig.ACCESS_KEY;\r6 qiniu.conf.SECRET_KEY = qnConf.QiniuConfig.SECRET_KEY;

赋值之后,就可以愉快的去要求upToken了!

写一只孤零零的单独小模块,用来天生upToken,代码长这样:

1 var qiniu = require('qiniu');\r2 \r3 function uptoken(bucketname) { // 指定一个bucket传名字进来\r4 var putPolicy = new qiniu.rs.PutPolicy(bucketname);\r5 return putPolicy.token;\r6 }\r7 \r8 exports.Uptoken = uptoken;

拿到upToken就可以华标致丽丽的开始上传了。

可以在刚才本地存储的POST要求成功后的回调里做。

代码就像酱紫:

1 // Do Qiniu upload in here:\r 2 var targetPath = path.resolve('./' + uploadDirName + '/' + filenameWithMd5); //接刚才的POST里的处理\r 3 var qiniu_uptoken = generateUptoken.Uptoken(qnConf.QiniuConfig.Bucket_Name);\r 4 var extra = null; // 放额外信息,先写null\r 5 fs.readFile(targetPath, function(error, data){\r 6 qiniu.io.put(qiniu_uptoken, uploadDirName + '/' + filenameForCloud, data, extra, function(err, ret){\r 7 if(err){\r 8 console.log('Something is wrong with Qiniu upload! ', err);\r 9 }else{\r10 console.log('qiniu: ', ret);\r11 console.log('Qiniu URL = ', qnConf.QiniuConfig.Domain + uploadDirName + '/' + filenameForCloud); //手动拼结果URL\r12 }\r13 });\r14 });

至此,七牛的上传也OK鸟!

撒花~~乐队起~~

5:后记

本文所述内容,仅限于最紧张最基本的逻辑,

未涉及页面的交互和部分非常相应的处理。

仅供参考。
表扔鸡蛋。

标签:

相关文章