国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

基于Node的React圖片上傳組件實現

cfanr / 2156人閱讀

摘要:常用的設置如下下的請求風格下的請求和不太一樣,在正式的請求發出之前都會先發一個類型為的請求作為試探,只有當該請求通過以后,正式的請求才能發向服務端。所以服務端路由我們還需要處理這樣一個請求注意該請求同樣需要設置跨域。

寫在前面

紅旗不倒,誓把JavaScript進行到底!今天介紹我的開源項目 Royal 里的圖片上傳組件的前后端實現原理(React + Node),花了一些時間,希望對你有所幫助。

前端實現

遵循React 組件化的思想,我把圖片上傳做成了一個獨立的組件(沒有其他依賴),直接import即可。

import React, { Component } from "react"
import Upload from "../../components/FormControls/Upload/"

//......

render() {
    return (
        
) }

uri 參數是必須傳的,是圖片上傳的后端接口地址,接口怎么寫下面會講到。

渲染頁面

組件render部分需要體現三個功能:

圖片選取(dialog窗口)

可拖拽功能(拖拽容器)

可預覽(預覽列表)

上傳按鈕 (button)

上傳完成圖片地址和鏈接 (信息列表)

render函數
render() {
    return (
        
this.handleChange(v)} type="file" size={this.state.size} name="fileSelect" accept="image/*" multiple={this.state.multiple} /> this.handleDragHover(e)} onDragLeave={(e)=>this.handleDragHover(e)} onDrop={(e)=>this.handleDrop(e)} className="upload-drag-area"> 或者將圖片拖到此處
{ this._renderPreview(); // 渲染圖片預覽列表 }
{ this._renderUploadInfos(); // 渲染圖片上傳信息 }
) }
渲染圖片預覽列表
_renderPreview() {
    if (this.state.files) {
        return this.state.files.map((item, idx) => {
            return (
                

{item.name}

{this.state.progress[idx]}
) }) } else { return null } }
渲染圖片上傳信息列表
_renderUploadInfos() {
    if (this.state.uploadHistory) {
        return this.state.uploadHistory.map((item, idx) => {
            return (
                

上傳成功,圖片地址是: 查看

); }) } else { return null; } }
文件上傳

前端要實現圖片上傳的原理就是通過構建FormData對象,把文件對象append()到該對象,然后掛載在XMLHttpRequest對象上 send() 到服務端。

獲取文件對象

獲取文件對象需要借助 input 輸入框的 change 事件來獲取 句柄參數 e

onChange={(e)=>this.handleChange(e)}

然后做以下處理:

e.preventDefault()
let target = event.target
let files = target.files
let count = this.state.multiple ? files.length : 1
for (let i = 0; i < count; i++) {
    files[i].thumb = URL.createObjectURL(files[i])
}
// 轉換為真正的數組
files = Array.prototype.slice.call(files, 0)
// 過濾非圖片類型的文件
files = files.filter(function (file) {
    return /image/i.test(file.type)
})

這時 files 就是 我們需要的文件對象組成的數組,把它 concat 到原有的 files里。

this.setState({files: this.state.files.concat(files)})

如此,接下來的操作 就可以 通過 this.state.files 取到當前已選中的 圖片文件。

利用Promise處理異步上傳

文件上傳對于瀏覽器來說是異步的,為了處理 接下來的多圖上傳,這里引入了 Promise來處理異步操作:

upload(file, idx) {
    return new Promise((resolve, reject) => {
        let xhr = new XMLHttpRequest()
        if (xhr.upload) {
            // 上傳中
            xhr.upload.addEventListener("progress", (e) => {
                // 處理上傳進度
                this.handleProgress(file, e.loaded, e.total, idx);
            }, false)
            // 文件上傳成功或是失敗
            xhr.onreadystatechange = (e) => {
                if (xhr.readyState === 4) {
                    if (xhr.status === 200) {
                    // 上傳成功操作
                    this.handleSuccess(file, xhr.responseText)
                    // 把該文件從上傳隊列中刪除
                    this.handleDeleteFile(file)
                    resolve(xhr.responseText);
                 } else {
                    // 上傳出錯處理 
                    this.handleFailure(file, xhr.responseText)
                    reject(xhr.responseText);
                 }
            }
        }
        // 開始上傳
        xhr.open("POST", this.state.uri, true)
        let form = new FormData()
        form.append("filedata", file)
        xhr.send(form)
    })
}
上傳進度計算

利用XMLHttpRequest對象發異步請求的好處是可以 計算請求處理的進度,這是fetch所不具備的。
我們可以為 xhr.upload 對象的 progress 事件添加事件監聽:

xhr.upload.addEventListener("progress", (e) => {
    // 處理上傳進度
    this.handleProgress(file, e.loaded, e.total, i);
}, false)

說明:idx參數是紀錄多圖上傳隊列的索引

handleProgress(file, loaded, total, idx) {
    let percent = (loaded / total * 100).toFixed(2) + "%";
    let _progress = this.state.progress;
    _progress[idx] = percent;
    this.setState({ progress: _progress })  // 反饋到DOM里顯示
}
拖拽上傳

拖拽文件對于HTML5來說其實非常簡單,因為它自帶的幾個事件監聽機制可以直接做這類處理。主要用到的是下面三個:

onDragOver={(e)=>this.handleDragHover(e)}
onDragLeave={(e)=>this.handleDragHover(e)}
onDrop={(e)=>this.handleDrop(e)}

取消拖拽時的瀏覽器行為:

handleDragHover(e) {
    e.stopPropagation()
    e.preventDefault()
}

處理拖拽進來的文件:

handleDrop(e) {
    this.setState({progress:[]})
    this.handleDragHover(e)
    // 獲取文件列表對象
    let files = e.target.files || e.dataTransfer.files
    let count = this.state.multiple ? files.length : 1
    for (let i = 0; i < count; i++) {
        files[i].thumb = URL.createObjectURL(files[i])
    }
    // 轉換為真正的數組 
    files = Array.prototype.slice.call(files, 0)
    // 過濾非圖片類型的文件
    files = files.filter(function (file) {
        return /image/i.test(file.type)
    })
    this.setState({files: this.state.files.concat(files)})
}
多圖同時上傳

支持多圖上傳我們需要在組件調用處設置屬性:

multiple = { true }  // 開啟多圖上傳 
size = { 50 }        // 一次最大上傳數量(雖沒有上限,為保證服務端正常,建議50以下)

然后我們可以使用 Promise.all() 處理異步操作隊列:

handleUpload() {
    let _promises = this.state.files.map((file, idx) => this.upload(file, idx))
    Promise.all(_promises).then( (res) => {
        // 全部上傳完成 
        this.handleComplete()
    }).catch( (err) => { console.log(err) })
}

好了,前端工作已經完成,接下來就是Node的工作了。

后端實現

為了方便,后端采用的是express框架來快速搭建Http服務和路由。具體項目見我的github node-image-upload。邏輯雖然簡單,但還是有幾個可圈可點的地方:

跨域調用

本項目后端采用的是express,我們可以通過 res.header() 設置 請求的 "允許源" 來允許跨域調用:

res.header("Access-Control-Allow-Origin", "*"); 

設置為 * 說明允許任何 訪問源,不太安全。建議設置成 你需要的 二級域名,如 jafeney.com

除了 "允許源" ,其他還有 "允許頭" 、"允許域"、 "允許方法"、"文本類型" 等。常用的設置如下:

function allowCross(res) {
    res.header("Access-Control-Allow-Origin", "*");   
    res.header("Access-Control-Allow-Headers", "Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild");
    res.header("Access-Control-Allow-Methods", "PUT, POST, GET, DELETE, OPTIONS");
    res.header("X-Powered-By"," 3.2.1")
    res.header("Content-Type", "application/json;charset=utf-8");
}
ES6下的Ajax請求

ES6風格下的Ajax請求和ES5不太一樣,在正式的請求發出之前都會先發一個 類型為 OPTIONS的請求 作為試探,只有當該請求通過以后,正式的請求才能發向服務端。

所以服務端路由 我們還需要 處理這樣一個 請求:

router.options("*", function (req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild");
    res.header("Access-Control-Allow-Methods", "PUT, POST, GET, DELETE, OPTIONS");
    res.header("X-Powered-By"," 3.2.1")
    res.header("Content-Type", "application/json;charset=utf-8");
    next();
});

注意:該請求同樣需要設置跨域。

處理上傳

處理上傳的圖片引人了multiparty模塊,用法很簡單:

/*使用multiparty處理上傳的圖片*/
router.post("/upload", function(req, res, next) { 
    // 生成multiparty對象,并配置上傳目標路徑
    var form = new multiparty.Form({uploadDir: "./public/file/"});
    // 上傳完成后處理
    form.parse(req, function(err, fields, files) {
        var filesTmp = JSON.stringify(files, null, 2);
        var relPath = "";
        if (err) {
            // 保存失敗 
            console.log("Parse error: " + err);
        } else {
            // 圖片保存成功!
            console.log("Parse Files: " + filesTmp);
            // 圖片處理
            processImg(files);
        }
    });
});
圖片處理

Node處理圖片需要引入 gm 模塊,它需要用 npm 來安裝:

npm install gm --save
BUG說明

注意:node的圖形操作gm模塊前使用必須 先安裝 imagemagickgraphicsmagickLinux (ubuntu)上使用apt-get 安裝:

    sudo apt-get install imagemagick
    sudo apt-get install graphicsmagick  --with-webp  // 支持webp格式的圖片

MacOS上可以用 Homebrew 直接安裝:

    brew install imagemagick
    brew install graphicsmagick --with-webp   // 支持webp格式的圖片  
預設尺寸

有些時候除了原圖,我們可能需要把原圖等比例縮小作為預覽圖或者縮略圖。這個異步操作還是用Promise來實現:

function reSizeImage(paths, dstPath, size) {
    return new Promise(function(resolve, reject) {
        gm(dstPath)
        .noProfile()
        .resizeExact(size)
        .write("." + paths[1] + "@" + size + "00." + paths[2], function (err) {
            if (!err) {
                console.log("resize as " + size + " ok!")
                resolve()
            }
            else {
                reject(err)
            };
        });
    });
}
重命名圖片

為了方便排序和管理圖片,我們按照 "年月日 + 時間戳 + 尺寸" 來命名圖片:

var _dateSymbol = new Date().toLocaleDateString().split("-").join("");
var _timeSymbol = new Date().getTime().toString();

至于圖片尺寸 使用 gmsize() 方法來獲取,處理方式如下:

gm(uploadedPath).size(function(err, size) {
    var dstPath = "./public/file/" + _dateSymbol + _timeSymbol 
        + "_" + size.width + "x" + size.height + "." 
        + _img.originalFilename.split(".")[1];
    var _port = process.env.PORT || "9999";
        relPath = "http://" + req.hostname + ( _port!==80 ? ":" + _port : "" ) 
        + "/file/" + _dateSymbol + _timeSymbol + "_" + size.width + "x" 
        +  size.height + "." + _img.originalFilename.split(".")[1];
    // 重命名
    fs.rename(uploadedPath, dstPath, function(err) {
        if (err) {
            reject(err)
        } else {
            console.log("rename ok!");
        }
    });
});
總結

對于大前端的工作,理解圖片上傳的前后端原理僅僅是淺層的。我們的口號是 "把JavaScript進行到底!",現在無論是 ReactNative的移動端開發,還是NodeJS的后端開發,前端工程師可以做的工作早已不僅僅是局限于web頁面,它已經滲透到了互聯網應用層面的方方面面,或許,叫 全棧工程師 更為貼切吧。

當然,全棧 兩個字的分量很重,不積跬步,無以至千里,功力低下的我還需要不斷修煉和實踐!

參考

張鑫旭 《基于HTML5的可預覽多圖片Ajax上傳》

@歡迎關注我的 github 和 個人博客 -Jafeney

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/80057.html

相關文章

  • 一些基于React、Vue、Node.js、MongoDB技術棧實踐項目

    摘要:利用中間件實現異步請求,實現兩個用戶角色實時通信。目前還未深入了解的一些概念。往后會寫更多的前后臺聯通的項目。刪除分組會連同組內的所有圖片一起刪除。算是對自己上次用寫后臺的一個強化,項目文章在這里。后來一直沒動,前些日子才把后續的完善。 歡迎訪問我的個人網站:http://www.neroht.com/? 剛學vue和react時,利用業余時間寫的關于這兩個框架的訓練,都相對簡單,有的...

    tangr206 評論0 收藏0
  • 第三方庫

    摘要:微信支付,支付寶支付,銀聯支付三大支付總結支付寶植入總結支付寶的植基于和百度地圖的組件庫基于百度地圖封裝的組件庫,使用這個庫最好需要先了解和百度地圖。 Commento - 多說 & Disqus 開源替代品 Commento - 多說 & Disqus 開源替代品 anime.js 簡單入門教程 強大輕量的動畫庫 anime.js 入門教程 來自B站的開源的MagicaSakura源...

    seanHai 評論0 收藏0
  • 第三方庫

    摘要:微信支付,支付寶支付,銀聯支付三大支付總結支付寶植入總結支付寶的植基于和百度地圖的組件庫基于百度地圖封裝的組件庫,使用這個庫最好需要先了解和百度地圖。 Commento - 多說 & Disqus 開源替代品 Commento - 多說 & Disqus 開源替代品 anime.js 簡單入門教程 強大輕量的動畫庫 anime.js 入門教程 來自B站的開源的MagicaSakura源...

    gityuan 評論0 收藏0
  • GitHub 值得收藏前端項目[每月更新...]

    摘要:也是一款優秀的響應式框架站點所使用的一套框架為微信服務量身設計的一套框架一組很小的,響應式的組件,你可以在網頁的項目上到處使用一個可定制的文件,使瀏覽器呈現的所有元素,更一致和符合現代標準。 GitHub 值得收藏的前端項目 整理與收集的一些比較優秀github項目,方便自己閱讀,順便分享出來,大家一起學習,本篇文章會持續更新,版權歸原作者所有。歡迎github star與fork 預...

    maxmin 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<