網(wǎng)頁聊天室之圖片上傳

圖片上傳包括:選擇指定圖片發(fā)送直接復(fù)制黏貼屏幕截圖發(fā)送

1. 發(fā)送選中的圖片

Node.js的Formidable模塊使用總結(jié)
  1. 創(chuàng)建Formidable.IncomingForm對象
    
var form = new formidable.IncomingForm();
  1. `form.encoding = 'utf-8'` 設(shè)置表單域的編碼
    
  2. form.uploadDir = "/my/dir"; 設(shè)置上傳文件存放的文件夾,默認(rèn)為系統(tǒng)的臨時文件夾,可以使用fs.rename()來改變上傳文件的存放位置和文件名
  3. `form.keepExtensions = false;` 設(shè)置該屬性為true可以使得上傳的文件保持原來的文件的擴(kuò)展名。
    
  4. form.type 只讀,根據(jù)請求的類型,取值'multipart' or 'urlencoded'
  5. `form.maxFieldsSize = 2 * 1024 * 1024;` 限制所有存儲表單字段域的大?。ǔile字段),如果超出,則會觸發(fā)error事件,默認(rèn)為2M
    
  6. form.maxFields = 1000 設(shè)置可以轉(zhuǎn)換多少查詢字符串,默認(rèn)為1000
  7. `form.hash = false; `設(shè)置上傳文件的檢驗碼,可以有兩個取值'sha1' or 'md5'.
    
  8. `form.multiples = false;` 開啟該功能,當(dāng)調(diào)用form.parse()方法時,回調(diào)函數(shù)的files參數(shù)將會是一個file數(shù)組,數(shù)組每一個成員是一個File對象,此功能需要 html5中multiple特性支持。
    
  9. form.bytesReceived 返回服務(wù)器已經(jīng)接收到當(dāng)前表單數(shù)據(jù)多少字節(jié)
  10. form.bytesExpected 返回將要接收到當(dāng)前表單所有數(shù)據(jù)的大小
  11. form.parse(request, [callback]) 該方法會轉(zhuǎn)換請求中所包含的表單數(shù)據(jù),callback會包含所有字段域和文件信息,如:
    form.parse(req, function(err, fields, files) {
      // ...   
    });
  1. form.onPart(part);你可以重載處理multipart流的方法,這樣做的話會禁止field和file事件的發(fā)生,你將不得不自己處理這些事情,如:
form.onPart = function(part) {
    part.addListener('data', function() {
        // ...
   });
}

如果你只想讓formdable處理一部分事情,你可以這樣做:

form.onPart = function(part) {
    if (!part.filename) {
         // 讓formidable處理所有非文件部分
     form.handlePart(part);
    }
}
  1. formidable.File對象
  • file.size = 0 上傳文件的大小,如果文件正在上傳,表示已上傳部分的大小
    *file.path = null 上傳文件的路徑。如果不想讓formidable產(chǎn)生一個臨時文件夾,可以在fileBegain事件中修改路徑
  • file.name = null 上傳文件的名字
  • file.type = null上傳文件的mime類型
  • file.lastModifiedDate = null時間對象,上傳文件最近一次被修改的時間
  • file.hash = null 返回文件的hash值
  • 可以使用JSON.stringify(file.toJSON())來格式化輸出文件的信息
  1. form.on('progress', function(bytesReceived, bytesExpected) {}); 當(dāng)有數(shù)據(jù)塊被處理之后會觸發(fā)該事件,對于創(chuàng)建進(jìn)度條非常有用。
  2. form.on('field', function(name, value) {});每當(dāng)一個字段/值對已經(jīng)收到時會觸發(fā)該事件
  3. form.on('fileBegin', function(name, file) {}); 在post流中檢測到任意一個新的文件便會觸發(fā)該事件
  4. form.on('file', function(name, file) {}); 每當(dāng)有一對字段/文件已經(jīng)接收到,便會觸發(fā)該事件
  5. form.on('error', function(err) {});當(dāng)上傳流中出現(xiàn)錯誤便會觸發(fā)該事件,當(dāng)出現(xiàn)錯誤時,若想要繼續(xù)觸發(fā)request的data事件,則必須手動調(diào)用request.resume()方法
  6. form.on('aborted', function() {}); 當(dāng)用戶中止請求時會觸發(fā)該事件,socket中的timeout和close事件也會觸發(fā)該事件,當(dāng)該事件觸發(fā)之后,error事件也會觸發(fā)
    21)form.on('end', function() {}); 當(dāng)所有的請求已經(jīng)接收到,并且所有的文件都已上傳到服務(wù)器中,該事件會觸發(fā)。此時可以發(fā)送請求到客戶端。

實際應(yīng)用

HTML
  <input type="file" id="uploadFile" multiple="multiple" accept="image/*" /> 
  <a class="btn btn-primary disabled" id="UploadBtn"><i class="fa fa-upload fa-fw"></i> 上傳圖片 </a>
JS中使用ajax
//發(fā)送圖片
function doUpload() {

    var file = $("#uploadFile")[0].files[0];
    var form = new FormData();
    form.append("file", file);

    $.ajax({
        url: "/p/uploadImg",
        type: "POST",
        data: form,
        async: true,
        processData: false,
        contentType: false,
        success: function(result) {
            startReq = false;
            if (result.code == 0) {
                var html = $.format(TO_MSG_IMG, result.data);
                $("#m"+fid).append(html);
                var msg = {
                    from: uid,
                    to: fid,
                    content: result.data
                };
                socket.emit('chat message', msg);
                toBottom();
            }
        }
    });
}
router
//上傳圖片
router.post('/uploadImg', function(req, res, next) {
    console.log("開始上傳");
    // var io = global.io;

    var form = new formidable.IncomingForm();
    var path = "";
    var fields = [];

    form.encoding = 'utf-8';                    //上傳文件編碼格式
    form.uploadDir = "public/uploadFile";     //上傳文件保存路徑(必須在public下新建)
    form.keepExtensions = true;                 //保持上傳文件后綴
    form.maxFieldsSize = 30000 * 1024 * 1024;   //上傳文件格式最大值

    var uploadprogress = 0;
    console.log("start:upload----"+uploadprogress);

    form.parse(req);

    form.on('field', function(field, value) {
        console.log(field + ":" + value);       //上傳的參數(shù)數(shù)據(jù)
    })
        .on('file', function(field, file) {
            path = '\\' + file.path;            //上傳的文件數(shù)據(jù)
        })
        .on('progress', function(bytesReceived, bytesExpected) {

            uploadprogress = (bytesReceived / bytesExpected * 100).toFixed(0);  //計算進(jìn)度
            console.log("upload----"+ uploadprogress);
            // io.sockets.in('sessionId').emit('uploadProgress', uploadprogress);
        })
        .on('end', function() {
            //上傳完發(fā)送成功的json數(shù)據(jù)
            console.log('-> upload done\n');
            entries.code = 0;
            entries.data = path;
            res.writeHead(200, {
                'content-type': 'text/json'
            });
            res.end(JSON.stringify(entries));
        })
        .on("err",function(err){
            var callback="<script>alert('"+err+"');</script>";
            res.end(callback);//這段文本發(fā)回前端就會被同名的函數(shù)執(zhí)行
        }).on("abort",function(){
        var callback="<script>alert('"+ttt+"');</script>";
        res.end(callback);
    });

});

2. 直接復(fù)制黏貼屏幕截圖發(fā)送

實現(xiàn)效果為:屏幕截圖, 直接黏貼到輸入框中, 點擊發(fā)送按鈕上傳

發(fā)送截圖.gif
HTML
  <a class="btn btn-primary disabled" id="UploadBtn-screen"><i class="fa fa-upload fa-fw"></i> 發(fā)送截圖 </a> 
  <a class="btn btn-primary disabled" id="cancelSend"><i class="fa fa-upload fa-fw"></i> 取消發(fā)送圖片 </a> 
  <div class="screen-shot" id="imgPreview"></div>
將網(wǎng)上搜到的封裝JS插件添加實際需求
//屏幕截圖
(function ($) {
    $.fn.screenshotPaste=function(options){
        var me = this;

        if(typeof options =='string'){
            var method = $.fn.screenshotPaste.methods[options];

            if (method) {
                return method();
            } else {
                return;
            }
        }

        var defaults = {
            imgContainer: '',   //預(yù)覽圖片的容器,
            uploadBtn: '',      //上傳按鈕,
            cancelBtn: '',      //取消按鈕,
            imgHeight: 200       //預(yù)覽圖片的默認(rèn)高度
        };

        options = $.extend(defaults,options);

        var imgReader = function( item ){
            var file = item.getAsFile();
            var reader = new FileReader();

            reader.readAsDataURL( file );
            reader.onload = function( e ){
                var img = new Image();

                img.src = e.target.result;

                $(img).css({ height: options.imgHeight });
                $(document).find(options.imgContainer)
                    .html('')
                    .show()
                    .append(img);
                $(document).find(options.uploadBtn).removeClass('disabled');
                $(document).find(options.cancelBtn).removeClass('disabled');
            };
        };
        //事件注冊
        $(me).on('paste',function(e){
            var clipboardData = e.originalEvent.clipboardData;
            var items, item, types;

            if( clipboardData ){
                items = clipboardData.items;

                if( !items ){
                    return;
                }

                item = items[0];
                types = clipboardData.types || [];

                for(var i=0 ; i < types.length; i++ ){
                    if( types[i] === 'Files' ){
                        item = items[i];
                        break;
                    }
                }

                if( item && item.kind === 'file' && item.type.match(/^image\//i) ){
                    imgReader( item );
                }
            }
        });

        $.fn.screenshotPaste.methods = {
            getImgData: function () {
                var src = $(document).find(options.imgContainer).find('img').attr('src');

                if(src==undefined){
                    src='';
                }

                return src;
            }
        };
    };
})(jQuery);

該插件目前只有一個方法 getImgData,調(diào)用示例如下:

var imgData = $('#imgPreview').screenshotPaste('getImgData');

該方法返回的是img的src屬性里面的內(nèi)容,即base64編碼的圖片數(shù)據(jù)內(nèi)容.

JS

將得到的base64編碼的圖片數(shù)據(jù)內(nèi)容通過AjaxPOST到服務(wù)器
利用隨機(jī)生成的字符串作為文件名

//屏幕截圖
$('#msg').screenshotPaste({
    imgContainer: '#imgPreview',    //預(yù)覽圖片的容器 
    uploadBtn: '#UploadBtn-screen',  //上傳按鈕
    cancelBtn: '#cancelSend'    //取消按鈕
});

// randomWord 產(chǎn)生任意長度隨機(jī)字母數(shù)字組合
// randomFlag 是否任意長度
// min 任意長度最小位[固定位數(shù)]
// max 任意長度最大位
function randomWord(randomFlag, min, max){
    var str = "",
        range = min,
        arr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];

    // 隨機(jī)產(chǎn)生
    if(randomFlag){
        range = Math.round(Math.random() * (max-min)) + min;
    }
    for(var i=0; i<range; i++){
        pos = Math.round(Math.random() * (arr.length-1));
        str += arr[pos];
    }
    return str;
}
//發(fā)送截圖
function doUploadScreen() {
    var imgData = $('#imgPreview').screenshotPaste('getImgData');
    var fileName = randomWord(false, 32);   //隨機(jī)字符串組成的圖片名
    
    $.ajax({
        type: "POST",
        url: "/p/upload",
        contentType: "application/json",
        dataType: "json",
        data: JSON.stringify({
            'img': imgData,
            'fileName': fileName
        }),
        success: function(result) {
            $('#imgPreview').hide();
            $('#UploadBtn-screen').addClass('disabled');
            $('#cancelSend').addClass('disabled');
            if (result.code == 0) {
                var html = $.format(TO_MSG_IMG, result.data);
                $("#m"+fid).append(html);
                var msg = {
                    from: uid,
                    to: fid,
                    content: result.data
                };
                socket.emit('chat message', msg);
                toBottom();
            }
        }
    })
}
router

Nodejs接收圖片base64格式保存為文件

base64的形式為“data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0。。。。”;當(dāng)接收到上邊的內(nèi)容后,需要將data:image/png;base64,這段內(nèi)容過濾掉,過濾成:“iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0。。?!保?/code>然后進(jìn)行保存。

利用fs.writeFile固定文件名前綴并將dataBuffer寫入

fs.writeFile("./public/uploadFile/upload_" + fileName +".jpg", dataBuffer, function(err) {}

//將文件名命名成這樣為了使用同意正則表達(dá)式
str = str.replace(/[\\]public[\\]uploadFile[\\]upload_[\w]+[\.]jpg/g,"<img class='img-msg' src=" + str + " />");

完整代碼:

//上傳截圖
router.post('/upload', function(req, res, next){
    //接收前臺POST過來的base64
    var imgData = req.body.img;
    //過濾data:URL
    var base64Data = imgData.replace(/^data:image\/\w+;base64,/, "");
    var dataBuffer = new Buffer(base64Data, 'base64');
    var fileName = req.body.fileName;
    //console.log(dataBuffer);

    fs.writeFile("./public/uploadFile/upload_" + fileName +".jpg", dataBuffer, function(err) {
        if(err){
            res.send(err);
        }else{
            var path = "\\public\\uploadFile\\upload_" + fileName +".jpg";
            entries.code = 0;
            entries.data = path;
            res.end(JSON.stringify(entries));
        }
    });
});

這里有個問題:當(dāng)截取的圖片過大時,無法 post 即 POST 413,所以:

express 如何解決 413 請求實體過長?
app.use(express.json({limit: '50mb'}));
app.use(express.urlencoded({limit: '50mb'}));

但在 Express 4里,必須要求有body-parser模塊和使用其json()urlencoded()方法,像這樣:

var bodyParser = require('body-parser');
app.use(bodyParser.json({limit: '50mb'}));
app.use(bodyParser.urlencoded({limit: '50mb', extended: true}));

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,554評論 19 139
  • 本文包括:1、文件上傳概述2、利用 Commons-fileupload 組件實現(xiàn)文件上傳3、核心API——Dis...
    廖少少閱讀 12,744評論 5 91
  • 本文為轉(zhuǎn)載,原文:Laravel項目中使用markdown編輯器及圖片粘貼上傳七牛云 Markdown Markd...
    ChainZhang閱讀 2,147評論 7 8
  • 一、幸福的人生活的方式都一樣 跟我在一起上班的明明,是我特別羨慕的人。她長的漂亮,爸爸媽媽恩愛無比,...
    Nayatoo閱讀 435評論 0 0
  • 滿塘春水弄風(fēng)清,撲面泠,池蛙鳴。 一朵花成,滴露暗香縈。 飛鳥每逐飛絮落,時有意,點浮萍。 柳絲釣起釣絲情,細(xì)竹傾...
    云鷲山人閱讀 1,322評論 0 3

友情鏈接更多精彩內(nèi)容