网页聊天室之图片上传

图片上传包括:选择指定图片发送直接复制黏贴屏幕截图发送

1. 发送选中的图片

Node.js的Formidable模块使用总结
  1. 创建Formidable.IncomingForm对象
    
var form = new formidable.IncomingForm();
  1. `form.encoding = 'utf-8'` 设置表单域的编码
    
  2. form.uploadDir = "/my/dir"; 设置上传文件存放的文件夹,默认为系统的临时文件夹,可以使用fs.rename()来改变上传文件的存放位置和文件名
  3. `form.keepExtensions = false;` 设置该属性为true可以使得上传的文件保持原来的文件的扩展名。
    
  4. form.type 只读,根据请求的类型,取值'multipart' or 'urlencoded'
  5. `form.maxFieldsSize = 2 * 1024 * 1024;` 限制所有存储表单字段域的大小(除去file字段),如果超出,则会触发error事件,默认为2M
    
  6. form.maxFields = 1000 设置可以转换多少查询字符串,默认为1000
  7. `form.hash = false; `设置上传文件的检验码,可以有两个取值'sha1' or 'md5'.
    
  8. `form.multiples = false;` 开启该功能,当调用form.parse()方法时,回调函数的files参数将会是一个file数组,数组每一个成员是一个File对象,此功能需要 html5中multiple特性支持。
    
  9. form.bytesReceived 返回服务器已经接收到当前表单数据多少字节
  10. form.bytesExpected 返回将要接收到当前表单所有数据的大小
  11. form.parse(request, [callback]) 该方法会转换请求中所包含的表单数据,callback会包含所有字段域和文件信息,如:
    form.parse(req, function(err, fields, files) {
      // ...   
    });
  1. form.onPart(part);你可以重载处理multipart流的方法,这样做的话会禁止field和file事件的发生,你将不得不自己处理这些事情,如:
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产生一个临时文件夹,可以在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) {}); 当有数据块被处理之后会触发该事件,对于创建进度条非常有用。
  2. form.on('field', function(name, value) {});每当一个字段/值对已经收到时会触发该事件
  3. form.on('fileBegin', function(name, file) {}); 在post流中检测到任意一个新的文件便会触发该事件
  4. form.on('file', function(name, file) {}); 每当有一对字段/文件已经接收到,便会触发该事件
  5. form.on('error', function(err) {});当上传流中出现错误便会触发该事件,当出现错误时,若想要继续触发request的data事件,则必须手动调用request.resume()方法
  6. form.on('aborted', function() {}); 当用户中止请求时会触发该事件,socket中的timeout和close事件也会触发该事件,当该事件触发之后,error事件也会触发
    21)form.on('end', function() {}); 当所有的请求已经接收到,并且所有的文件都已上传到服务器中,该事件会触发。此时可以发送请求到客户端。

实际应用

HTML
   
   上传图片 
JS中使用ajax
//发送图片
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);       //上传的参数数据
    })
        .on('file', function(field, file) {
            path = '\\' + file.path;            //上传的文件数据
        })
        .on('progress', function(bytesReceived, bytesExpected) {

            uploadprogress = (bytesReceived / bytesExpected * 100).toFixed(0);  //计算进度
            console.log("upload----"+ uploadprogress);
            // io.sockets.in('sessionId').emit('uploadProgress', uploadprogress);
        })
        .on('end', function() {
            //上传完发送成功的json数据
            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="";
            res.end(callback);//这段文本发回前端就会被同名的函数执行
        }).on("abort",function(){
        var callback="";
        res.end(callback);
    });

});

2. 直接复制黏贴屏幕截图发送

实现效果为:屏幕截图, 直接黏贴到输入框中, 点击发送按钮上传

网页聊天室之图片上传_第1张图片
发送截图.gif
HTML
   发送截图  
   取消发送图片  
  
将网上搜到的封装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: '',   //预览图片的容器,
            uploadBtn: '',      //上传按钮,
            cancelBtn: '',      //取消按钮,
            imgHeight: 200       //预览图片的默认高度
        };

        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,调用示例如下:

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

该方法返回的是img的src属性里面的内容,即base64编码的图片数据内容.

JS

将得到的base64编码的图片数据内容通过AjaxPOST到服务器
利用随机生成的字符串作为文件名

//屏幕截图
$('#msg').screenshotPaste({
    imgContainer: '#imgPreview',    //预览图片的容器 
    uploadBtn: '#UploadBtn-screen',  //上传按钮
    cancelBtn: '#cancelSend'    //取消按钮
});

// randomWord 产生任意长度随机字母数字组合
// randomFlag 是否任意长度
// min 任意长度最小位[固定位数]
// 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'];

    // 随机产生
    if(randomFlag){
        range = Math.round(Math.random() * (max-min)) + min;
    }
    for(var i=0; i
router

Nodejs接收图片base64格式保存为文件

base64的形式为“data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0。。。。”;当接收到上边的内容后,需要将data:image/png;base64,这段内容过滤掉,过滤成:“iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0。。。”;然后进行保存。

利用fs.writeFile固定文件名前缀并将dataBuffer写入

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

//将文件名命名成这样为了使用同意正则表达式
str = str.replace(/[\\]public[\\]uploadFile[\\]upload_[\w]+[\.]jpg/g,"");

完整代码:

//上传截图
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));
        }
    });
});

这里有个问题:当截取的图片过大时,无法 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}));

你可能感兴趣的:(网页聊天室之图片上传)