Node.js——微信聊天图片发送功能实现

图片实现功能分浏览器实现

这次主要要写如何通过右键粘贴获得截图(没错,就是QQ截图)以及网络图片

chrome

$("body").on('paste','.emoji-wysiwyg-editor',function(event){

        var clipboardData = (event.clipboardData || event.originalEvent.clipboardData);
        //chrome提供了直接获取剪切板上内容的API,有的chrome浏览器是event.clipboardData,有的则是event.originalEvent.clipboardData
        if ( clipboardData.items ) {
            var  items = clipboardData.items,
                len = items.length,
            //如果粘贴纯文本,那么len=1,如果粘贴网页图片,len=2, items[0].type = 'text/plain', items[1].type = 'image/*'
            //如果使用截图工具粘贴图片,len=1, items[0].type = 'image/png'
                blob = null;

            for (var i = 0; i < len; i++) {
                console.log(items[i]);
                console.log( items[i].type);
                if (items[i].type.indexOf("image") !== -1) {
                    event.preventDefault();
                    blob = items[i].getAsFile();
                    //blob就是需要上传到服务器的内容
                }
            }
            console.log(blob);
            if ( blob !== null ) {
                var reader = new FileReader();
                reader.onload = function (event) {
                //文件读取成功完成时触发
                    // event.target.result 即为图片的Base64编码字符串
                    var base64_str = event.target.result;
                    //console.log(base64_str);
                    //根据后端服务器来选择上传base64的编码或者直接上传blob对象
                    doUpload(blob);
                }
                reader.readAsDataURL(blob);
                //该方法将文件读取为一段以 data: 开头的字符串,这段字符串的实质就是 Data URL,Data URL是一种将小文件直接嵌入文档的方案。这里的小文件通常是指图像与 html 等格式的文件。
            }
       }

})


//ajax

function doUpload(file) {

    $(".pg-wrapper").show();

    var file = file;
    //获取img的后缀,png、jpg等
    console.log(file.type);
    var form = new FormData();
    form.append("file", file);
    form.append("filetype",file.type);
    console.log(form);

    $.ajax({
        url: "/uploadImg",
        type: "POST",
        data: form,
        async: true,
        processData: false,
        contentType: false,
        success: function(result) {
            $.Insertimage(result.data);
            //图片上传到服务器后台成功后返回图片的地址,Insetimage用于插入图片
        }
    });
        //$.Insertimage补充于jquery-emojirea.js这个闭包函数中
        $.Insertimage= function (url) {
        if (CONST.hasFocus) {
            CONST.selection = util.saveSelection();
                        //CONST指向EmojiArea_WYSIWYG(上一篇文章提过,这个函数写在jquery-emojirea.js这个闭包函数中)
                        //如果文本编辑框div被处于focus状态,则调用 util.saveSelection()函数获取当前光标对象并复制给CONST.selection
        }
        var $img = $(EmojiArea.createImage(url));
                //将url包装成为<img src=url>的格式
        if ($img[0].attachEvent) {
            $img[0].attachEvent('onresizestart', function(e) { e.returnValue = false; }, false);
        }
        console.dir(CONST.$editor+"这里是const");
        CONST.$editor.trigger('focus');
        if (CONST.selection) {
            util.restoreSelection(CONST.selection);
                        //将文本编辑框的selection(也可以是历史记录)存入util中,设置当前selection为CONST.selection
        }
        try { util.replaceSelection($img[0]); } catch (e) {}
                //对光标部分进行替换
        CONST.onChange();
                //onChange方法用于将div的内容赋值给对应的textarea中
    }

}

对应后台程序

后台用的是Node.js以及express

var formidable = require('formidable');
var fs = require('fs');
router.post('/uploadImg', function(req, res, next) {

  var io = global.io;
//var io = require('socket.io').listen(server);global.io = io;,io是全局变量

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

  form.encoding = 'utf-8';
  form.uploadDir = "upload";
  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) {
    fields[field] = value;
    //主要用于存储filetype
  })
      .on('file', function(field, file) {
        path = '\\' + file.path;
        if(fields['filetype'].match(/image/)){
          var str = fields['filetype'].match(/[^image\/]\w+$/);//提取image/后面部分的图片后缀
          var str = str[0];
          var reg = new RegExp(str);
          if(!file.path.match(reg)){
          //如果file的路径不含图片后缀则执行以下操作
            fs.rename(file.path,file.path+'.'+str, function(err){
              if(err){
                throw err;
              }
              console.log('done!');
              path = '\\' + file.path+'.'+str;
            })
          }else{
            path = '\\' + file.path;
          }
        }else{
          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() {

        //console.log('-> upload done\n');
        entries.code = 0;

        if(fields['filetype'].match(/image/)){
          var str = fields['filetype'].match(/[^image\/]\w+$/);
          var str = str[0];
          var reg = new RegExp(str);
          if(!path.match(reg)){
            path = path + '.' + str;
          }
        }//再次检查path是否正确(其实上面一段检查并没有什么卵用)


        entries.data = path;
        res.writeHead(200, {
          'content-type': 'text/json'
        });
        res.end(JSON.stringify(entries));//将图片地址作为json数据传送到前台
      })
      .on("err",function(err){
        var callback="<script>alert('"+err+"');</script>";
        res.end(callback);//这段文本发回前端就会被同名的函数执行
      }).on("abort",function(){
    var callback="<script>alert('"+ttt+"');</script>";
    res.end(callback);
  });

});

firefox

$("body").on('paste','.emoji-wysiwyg-editor',function(event){
           setTimeout(function () {
                //设置setTimeout的原因是为了保证图片先插入到div里,然后去获取值
                $(".emoji-wysiwyg-editor img").each(function () {
                    if($(this).attr("src").toString().match(/base64/)){//火狐浏览器不提供获取clipboard内容的api,但是允许粘贴图片到可编辑的div中,粘贴后的图片的src是base64编码的形式,可将其上传到服务器
                        var base64_str = $(this).attr("src");
                        doUploadFF(base64_str);
                    }else if($(this).attr("alt")===undefined||!$(this).attr("alt").toString().match(/^:.+:$/))
                    {
                        var base64_str = $(this).attr("src");
                        doUploadFF(base64_str);
                    }
                    //两个if语句第一个用于获取截图的到的本地图片,第二个用于获取网页上的图片复制过来的,貌似没差
                })
            }, 1);
})
//ajax
function doUploadFF(base64_str) {

    console.log(base64_str);

    $.ajax({
        url: "/uploadImgFF",
        type: "POST",
        data:JSON.stringify({
            'base64':base64_str,
        }),
        async: true,
        processData: false,
        contentType:"application/json",
        success: function(result) {
            console.log(result.store_path);
            //$.Insetimage(result.data);
            $(".emoji-wysiwyg-editor img").each(function () {
                if($(this).attr("src").toString().match(/base64/)){
                    $(this).attr({src:result.store_path,alt:":"+result.store_path+":"});
                    $(this).addClass("Notemoji");
                    $.constChange();
                }else if($(this).attr("alt")===undefined||!$(this).attr("alt").toString().match(/^:.+:$/))
                {
                    $(this).attr({src:result.store_path,alt:":"+result.store_path+":"});
                    $(this).addClass("Notemoji");
                    $.constChange();
                }
            })
        }
    });
}

后台

router.post('/uploadImgFF',function (req,res,next) {
  //1.获取客户端传来的src_str字符串=>判断是base64还是普通地址=>获取图片类型后缀(jpg/png etc)
//=>如果是base64替换掉"前缀"("data:image\/png;base64," etc)
//2.base64 转为 buffer对象 普通地址则先down下来
//3.写入硬盘(后续可以将地址存入数据库)
//4.返回picture地址

  var src_str = req.body.base64,
      timestamp = new Date().getTime();
  console.log(src_str);

  if ( src_str.match(/^data:image\/png;base64,|^data:image\/jpg;base64,|^data:image\/jpg;base64,|^data:image\/bmp;base64,/) ) {
    //处理截图 src_str为base64字符串

    var pic_suffix = src_str.split(';',1)[0].split('/',2)[1],
        base64 = src_str.replace(/^data:image\/png;base64,|^data:image\/jpg;base64,|^data:image\/jpg;base64,|^data:image\/bmp;base64,/, ''),
        buf = new Buffer(base64, 'base64'),
        store_path = './upload/' + timestamp + '.' + pic_suffix;
        console.log(store_path);

    fs.writeFile(store_path, buf, function (err) {
      if (err) {
        throw err;
      } else {
        res.json({'store_path': store_path});
      }
  });
} else {
  console.log("网页图片"+src_str);
  var temp_array = src_str.split('.'),
      pic_suffix = temp_array[temp_array.length - 1],
      store_path = './upload/' + timestamp + '.' + pic_suffix,
      wstream = fs.createWriteStream(store_path);
  console.log(store_path);
  request(src_str).pipe(wstream);
  wstream.on('finish', function (err) {
    console.log("进入finish");
    if( err ) {
      throw err;
    } else {
      res.json({'store_path': store_path});
    }
  });


}
})
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,057评论 25 707
  • WebSocket-Swift Starscream的使用 WebSocket 是 HTML5 一种新的协议。它实...
    香橙柚子阅读 23,556评论 8 183
  • 小时候,在红薯刚刚可以吃的时候,我们就闹腾着要吃。于是父母有时候上地里干活回来,会扒上几个拿回家,给我们尝鲜。其实...
    枫红云天阅读 872评论 20 14
  • 实现后台自动生成文档,减少前后台沟通成本。spring mvc与swagger的集成 1. 下载https://g...
    晨宇阅读 1,539评论 2 2
  • 陈元达反对刘聪修建昭仪殿,他估计肯定要出事,所以拿锁把自己锁起来。这就是,有孚,血去惕出,无咎。通过锁起来搏一把,...
    Nuckyli阅读 177评论 0 0