日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

Node开发文件上传系统及向七牛云存储和亚马逊AWS S3的文件上传

發布時間:2023/12/13 windows 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Node开发文件上传系统及向七牛云存储和亚马逊AWS S3的文件上传 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

背景起,有奏樂:

有偉人曰:學習技能的最好途徑莫過于理論與實踐相結合。

初學Node這貨時,每每讀教程必會Fall asleep。

當真要開發系統時,頓覺精神百倍,即便踩坑無數也不失斗志。

因為同團隊的小伙伴們都在辛勤工作,正是因為他們的工作,

才讓我有足夠的時間拖著我疲軟的智商來研究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'); 3 var multipartMiddleware = multipart(); 5 var express = require('express'); 7 var router = express.Router(); 9 router.post( ‘/saveInLocalServer’, multipartMiddleware, function(req, res){ 。。。});

這個請求接收的是從前端的Plupload上傳的File,

神秘的中間件會在服務器生成臨時文件,但不會刪除它們。

因此在處理的最后要手動刪除臨時文件req.files。How to?

收到請求后,處理文件的部分代碼如下:

1 var file = req.files.file; 2 var tempPath = file.path, 3 fileName = file.name, 4 fileType = file.type, 5 fileSize = file.size; 6 var uploadDirName = dirName.DirName; // 生成目錄的模塊,每月一生 7 var filenameWithMd5 = MD5( new Date().getTime() ) + '-' + fileName; 8 var filenameForCloud = fileRename.FileRename(fileName); 9 // 保存到本地服務器的文件,使用MD5重命名文件 10 // 上傳到云存儲的文件,使用自定義的模塊重命名 11 var targetPath = path.resolve('./' + uploadDirName + '/' + filenameWithMd5); 12 // Save file in our local server: 13 fs.rename(tempPath, targetPath, function(err, data){ 14 if( err ){ 15 var result = 'error'; 16 res.status( result ).send(); 17 } else { 18 var result = 'ok'; 19 var uploadInfos = { ... }; // AWS的config信息定義在服務端,由模塊引入并發送到前端,供JS接口調用: 20 res.status( result ).send( uploadInfos ); 21 // Next do Qi Niu Upload ... blah blah blah 22 } 23 });

針對上述代碼的幾處說明:

a:關于在本地服務器生成目錄:

我們的需求是,每月首次觸發上傳動作時,在服務器創建一只新目錄。

該月內的其余上傳文件,都存儲在這一目錄里。

所有的文件會按上傳時間,以自然月為目錄而分類。

按月創建目錄的邏輯,我寫了一枚小小模塊,如下:

var fs = require('fs');var _d = new Date(); var _year = _d.getFullYear(); var _month = (_d.getMonth() + 1 < 10)?('0' + (_d.getMonth() + 1)):(_d.getMonth() + 1); // 為整齊,月份都顯示為兩位數,因此1-9月前面加0 var dir = _year + '-' + _month + '-alex_upload';if (!fs.existsSync(dir)){fs.mkdirSync(dir); }exports.DirName = dir; // 輸出模塊名為DirName// ============================ // 假設這個文件名為makeDirName.js,則在業務邏輯中引入并應用要這樣: var d_name = require('../routes/makeDirName'); var someName = d_name.DirName; // 輸出的模塊名在這里被這樣引用

b:關于文件重命名:

我們的需求是,存在本地服務器的文件,使用MD5重命名。

上傳到云存儲的文件,使用時間戳和隨機字符串共同重命名。

重命名文件的模塊是醬紫寫的:

1 function rename( filename ) { 2 var name = ''; 3 var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; 4 var length = 6; // 隨機字符串的長度,暫用6 5 for(var i = 0; i < length; i++){ 6 name += possible.charAt( Math.floor(Math.random() * possible.length) ); 7 } 8 var timestamp = new Date().getTime(); 9 name = timestamp + '-' + name + '-' + filename; 10 return name; 11 }; 12 13 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({ 2 runtimes: 'html5,flash,silverlight,html4', 3 file_data_name: 'file', 4 container: _SCOPE.containerId, 5 browse_button: _SCOPE.filePickerId, 6 uptoken_url: _ELE.fileUptoken.innerHTML, 7 url: _ELE.fileLocalSave.innerHTML, 8 flash_swf_url: _SCOPE.swfUrl, 9 silverlight_xap_url:_SCOPE.xapUrl, 10 filters: { 11 max_file_size: _SCOPE.maxFileSize, 12 mime_types: [ 13 {title: 'Image files', extensions: 'jpg,png,gif'}, 14 {title: 'Zip files', extensions: 'zip'} 15 ] 16 }, 17 init: {...} 18 });

這里的 _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"?> 2 <CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> 3 <CORSRule> 4 <AllowedOrigin>http://localhost:3038</AllowedOrigin> //本地測試入口 5 <AllowedOrigin>http://shaojing.wang</AllowedOrigin> //線上測試入口 6 <AllowedMethod>PUT</AllowedMethod> //可執行的方法 7 <AllowedMethod>DELETE</AllowedMethod> //可執行的方法 8 <MaxAgeSeconds>3000</MaxAgeSeconds> 9 <ExposeHeader>x-amz-server-side-encryption</ExposeHeader> 10 <ExposeHeader>x-amz-request-id</ExposeHeader> 11 <ExposeHeader>x-amz-id-2</ExposeHeader> 12 <AllowedHeader>*</AllowedHeader> 13 </CORSRule> 14 </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'); 2 var qnConf = require('../config/qiniu_config'); 3 4 /* Prepare Qiniu config, we make Qiniu upload in Node Server not in browser*/ 5 qiniu.conf.ACCESS_KEY = qnConf.QiniuConfig.ACCESS_KEY; 6 qiniu.conf.SECRET_KEY = qnConf.QiniuConfig.SECRET_KEY;

賦值之后,就可以開心的去請求upToken了!

寫一只孤零零的單獨小模塊,用來生成upToken,代碼長這樣:

1 var qiniu = require('qiniu'); 2 3 function uptoken(bucketname) { // 指定一個bucket傳名字進來 4 var putPolicy = new qiniu.rs.PutPolicy(bucketname); 5 return putPolicy.token(); 6 } 7 8 exports.Uptoken = uptoken;

拿到upToken就可以華麗麗麗麗的開始上傳了。

可以在剛才本地存儲的POST請求成功后的回調里做。

代碼就像醬紫:

1 // Do Qiniu upload in here: 2 var targetPath = path.resolve('./' + uploadDirName + '/' + filenameWithMd5); //接剛才的POST里的處理 3 var qiniu_uptoken = generateUptoken.Uptoken(qnConf.QiniuConfig.Bucket_Name); 4 var extra = null; // 放額外信息,先寫null 5 fs.readFile(targetPath, function(error, data){ 6 qiniu.io.put(qiniu_uptoken, uploadDirName + '/' + filenameForCloud, data, extra, function(err, ret){ 7 if(err){ 8 console.log('Something is wrong with Qiniu upload! ', err); 9 }else{ 10 console.log('qiniu: ', ret); 11 console.log('Qiniu URL = ', qnConf.QiniuConfig.Domain + uploadDirName + '/' + filenameForCloud); //手動拼結果URL 12 } 13 }); 14 });

至此,七牛的上傳也OK鳥!

撒花~~樂隊起~~

?

5:后記

本文所述內容,僅限于最主要最基本的邏輯,

未涉及頁面的交互和部分異常響應的處理。

僅供參考。表扔雞蛋。

?

轉載于:https://www.cnblogs.com/alex1128/p/nodeToUpload.html

總結

以上是生活随笔為你收集整理的Node开发文件上传系统及向七牛云存储和亚马逊AWS S3的文件上传的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。