网页的实时聊天中实现粘贴板发送图片

2018-04-28  本文已影响205人  tangyefei

本篇主要介绍了通过获取剪贴板中的图片内容,上传文件内容到七牛中,然后使用返回的图片地址来实现在线聊天的图片发送。要完整地运行其中的例子,需要项目具备的接收和发送消息的逻辑。主要内容结构如下

需求分析

在项目的在线聊天功能中,已经具备了发送文字和图片的功能,为了方便用户使用,要求在用户截取图片/复制图片以后,直接在聊天的输入框中粘贴,点击确定即可直接发送图片。微信的网页版在线聊天中有类似的效果如下:

image.png

在很多站点中,也有类似的应用,比如在线编辑文档/帖子的回复输入框中,想要通过这种方式插入图片都会用到这样的实现。

过程分析

为了做到简明,用伪码来表述这个过程:

document.paste => (pastEvent) {
  file = getLastOneFilePasted(pastEvent);
  reader = createFileReader();
  reader.onload => (loadEvent) {
      content = getLoadContent(loadEvent)
       uploadContentToQiniu( (response) => { // callback
          url = formatImageUrl(response)
          sendImg(img)
        })
  }
  reader.read(file);
}

实现过程

获取文件内容

为了方便引入了jquery,如下代码能够实现在控制台打印获粘贴的文件内容。注:这里获取的粘贴的文件列表是一个数组,为了简化处理,我们只把粘贴的最后一张图片进行发送。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="jquery.js"></script>
  <style>
    textarea {
      width: 400px;
      height: 160px;
    }
  </style>
</head>
<body>
  <textarea class="chat-textarea"></textarea>
  <script>
      document.onpaste = function (event) {
        if($(event.target).hasClass('chat-textarea')){
          let items = (event.clipboardData || event.originalEvent.clipboardData).items;
          let item = items[items.length - 1];
          if (item.kind === 'file' && item.type == "image/png") {
            var blob = item.getAsFile();
            var reader = new FileReader();
            reader.onload = function (event) {
              console.log(event.target.result);
            };
            reader.readAsDataURL(blob);
          }
        }
      };
  </script>
</body>
</html>

其中onpaste获取的event对象如下,我们可以通过items都可以获取到粘贴的图片的内容,这里items有两个内容,一个是张贴的图片名字,另一个是截图生成的png。我们需要只对png类型的图片做处理。

如下为控制台打印的数出内容:

可以看到输出内容格式形如如下格式的内容:

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAsgAAAKyCAYAAADIEijRAAAgAElEQVR4AeydBZhkxfX2T+vIugssurgnhBAggUCIQIy4EuMj7gnEXYj7P24QIUYUCSEEghNcFltgYWHdZ0fav+dXPdVz587tnu7Rnpn3PM9Md99bt+Ste0+9derUubHOjm2lwuarrbTpcosXuyzZOsPiyZR5KRULls90WiFfsticI23GPq/3p/QpBISAEBACQkAICAEhIAQmHQJxWpSYd5zF2ve2YrFkhXzGSqVipaGxeMLiyVazUs5KHfdVjuuLEBAC

这种格式成为data URI scheme(wiki),它作为内容格式提供了在网页中直接内容的能力,这样就不需要多个HTTP请求单独的文件了。其中前面是属于定式,其中base64表示data部分是二进制文件,使用ASCII格式处理并采用了Base64进行binary-to-text的转化。

将文件上传到七牛并获得文件地址

要获得七牛的存储空间,首先要注册一个七牛开发者账号,并拿到AccessKey/SecretKey用于生成token。另外为了上传,你还需要创建自己的存储空间

七牛的上传分为客户端上传和服务器端上传,我们采用个但是客户端上传。首先在客户端也就是网页端引入qiniu.js,最简单的方式是引用remote js,这里我们引入最新版本的2.2.2版:

<script src="https://unpkg.com/qiniu-js@2.2.2/dist/qiniu.min.js"></script>

其次是客户端的token生成。为了保证安全性,我们要先获取一个由后台生成的上传token,它通常是由后台api实时提供。我们直接使用七牛提供的node.js示例用于执行得到token。

首先初始化一个项目,然后安装qiniu:

$ npm init -y
$ npm install --save qiniu

然后新建一个文件token.js

//token.js
var qiniu = require("qiniu");

var accessKey = 'Your accessKey';
var secretKey = 'Your secretKey';
var mac = new qiniu.auth.digest.Mac(accessKey, secretKey);
var options = {
  scope: "Your Scope",
};
var putPolicy = new qiniu.rs.PutPolicy(options);
var uploadToken=putPolicy.uploadToken(mac);
console.log(uploadToken)

通过执行 node token.js得到可用的上传token(这里只是为了方便演示,在实际开发中需要由后台提供实时的token)。

参考官方的例子书写我们的上传图片的JavaScript代码:

// 此处是华东空间的上传路径,自己创建空间如果非华东可能会有些不同
let putUrl = "http://up.qiniu.com/putb64/-1"; 
// download domain可以在自己创建的上传空间中看到
let downloadDomain = "Your download domain";
// 通过token.js代码生成的值
let uploadToken = "Your upload token";

function putb64(pic, token, callback){
  var xhr = new XMLHttpRequest();
  xhr.onreadystatechange=function(){
    if (xhr.readyState==4){
      var response = JSON.parse(xhr.responseText)
      callback(downloadDomain + response.key)
    }
  }
  xhr.open("POST", putUrl, true);
  xhr.setRequestHeader("Content-Type", "application/octet-stream");
  xhr.setRequestHeader("Authorization", `UpToken ${token}`);
  xhr.send(pic);
}

关于header设置的Content-Type可以参考这篇进行了解。

在前例中我们已经得到了图片对内容,只需要裁切掉前置部分,提取图片内容用于调用put64即可:

var content = event.target.result;
// 去除非内容部分
// 注:不同media type的的data URI需要裁切的字符数可能会不同
var pic = content.substr(22);

putb64(pic, token, (url) => {
// use the url for custom use
});

得到图片的url以后,就可以结合业务代码进行IM相关的发送逻辑了。

最后给出一个完整的示例如下:

//token.js 
var qiniu = require("qiniu");

var accessKey = 'Your accessKey';
var secretKey = 'Your secretKey';
var mac = new qiniu.auth.digest.Mac(accessKey, secretKey);
var options = {
  scope: "Your Scope",
};
var putPolicy = new qiniu.rs.PutPolicy(options);
var uploadToken=putPolicy.uploadToken(mac);
console.log(uploadToken)
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="jquery.js"></script>
  <script src="https://unpkg.com/qiniu-js@2.2.2/dist/qiniu.min.js"></script>
  <style>
    textarea {
      width: 400px;
      height: 160px;
    }
  </style>
</head>
<body>
  <textarea class="chat-textarea"></textarea>
  <script>
    let putUrl = "http://up.qiniu.com/putb64/-1"; //仅适用于华东的存储空间
    let downloadDomain = 'Your download domain';
    let uploadToken = "Your upload token";

      function putb64(pic, token, callback){
        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange=function(){
          if (xhr.readyState==4){
            var response = JSON.parse(xhr.responseText)
            callback(downloadDomain + response.key)
          }
        }
        xhr.open("POST", putUrl, true);
        xhr.setRequestHeader("Content-Type", "application/octet-stream");
        xhr.setRequestHeader("Authorization", `UpToken ${token}`);
        xhr.send(pic);
      }

      document.onpaste = function (event) {
        if($(event.target).hasClass('chat-textarea')){
          let items = (event.clipboardData || event.originalEvent.clipboardData).items;
          let item = items[items.length - 1];
          if (item.kind === 'file' && item.type == "image/png") {
            var blob = item.getAsFile();
            var reader = new FileReader();

            reader.onload = function (event) {
              var token = uploadToken;
              var content = event.target.result;
              var pic = content.substr(22);

              putb64(pic, token, (url) => {
                var img = document.createElement(`img`);
                img.setAttribute('src', url);
                document.body.append(img)
              });
            };
            reader.readAsDataURL(blob); 
          }
        }
      };
  </script>
</body>
</html>
上一篇下一篇

猜你喜欢

热点阅读