前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >复制粘贴那些事

复制粘贴那些事

作者头像
一粒小麦
发布2019-08-20 12:07:04
2.3K0
发布2019-08-20 12:07:04
举报
文章被收录于专栏:一Li小麦一Li小麦

需求

这篇公众号文章是用typora上写的,这是一款大名鼎鼎的客户端markdown编辑器。

Markdown的语法简洁明了、学习容易,而且功能比纯文本更强,因此有很多人用它写博客。世界上最流行的博客平台WordPress和大型CMS如Joomla、Drupal都能很好的支持Markdown。完全采用Markdown编辑器的博客平台有Ghost和Typecho。 除此之外,由于我们有了RStudio这样的神级编辑器,我们还可以快速将Markdown转化为演讲PPT、Word产品文档、LaTex论文甚至是用非常少量的代码完成最小可用原型。在数据科学领域,Markdown已经广泛使用,极大地推进了动态可重复性研究的历史进程。

有了markdown,我彻底抛弃了word。

但是markdown的图片正常来说需要联网。尤其是公开发布的文档。联网时必备的条件。

市面上也有很多markdown编辑软件。我也在寻求好用的markdown编辑器。

我尝试过保存到为知笔记/网易云笔记/印象笔记。但是体验其实都非常差。尤其是我这种长篇累牍都文章。到最后都非常卡。

不知你是否用过博客园的markdown编辑器?印象最深的相信就是"丑陋"吧。但是博客园有一个很好的功能,就是使用微信或qq截图之后,ctrl+v,就自动生成了markdown格式的链接地址。这是其他笔记应用没有的。

![](https://img2018.cnblogs.com/blog/1011161/201908/1011161-20190814204338058-791418648.png)

这段markdown代码在经过渲染后就是一张图了。

我很喜欢如是这般把博客园作为自己的图床。直到某天外网访问它403了。

现在有了服务器,还写了一款markdown编辑器用于发布文档。

就自己实现一个图床吧!

项目基于vue,不过为了阐述方便,尽可能原生语法。

编辑器复制粘贴,起码发生以下事情:

  • 获取文件对象
  • 前端压缩图片文件算法
  • 服务器配置七牛cdn
  • 返回文件地址

获取文件对象

Clipboard API的Clipboard接口提供了一种读写操作系统剪贴板的方式。

而剪切板事件(copy/paste/cut)是这里涉及的主要的方法。

获取剪切板的内容可以用一个全局api来拿:window.setSelection().toString()

首先是要监听你的paste事件:

假设div##markdownContent是你要粘贴到区域:

// 二次开发内容
let markdownContent= document.querySelector('#markdownContent');
document.addEventListener('paste', function (event) {
    var items = event.clipboardData && event.clipboardData.items;
    var file = null;
    if (items && items.length) {
        // 检索剪切板items
        for (var i = 0; i < items.length; i++) {
            if (items[i].type.indexOf('image') !== -1) {
                file = items[i].getAsFile();
                break;
            }
        }
    }

      if(file){
      // 此时file就是剪切板中的图片文件
        console.log('file',file);

      // ...
    }

});

图片处理

七牛提供了上传压缩的服务。这个服务是付费的。当甲方比较穷,可以考虑在前端把它转小了再传。

原理是拿到file对象之后,new一个FileReader,以base64的形式读取。

const reader = new FileReader();
reader.readAsDataURL(file);

然而reader加载读取是需要时间的。为了优雅的编码。封装一个方法:

const getBase64 = () => {
    return new Promise((resolve, reject) => {
        reader.addEventListener('load', (e) => {
            resolve(e.target.result);
        });
    });
};

接下来就很舒服了。

let base64 = await getBase64();
压缩

base64是不会压缩图像质量的。但base64是canvas对象很喜欢的格式。

总的思路就是,把你粘贴的图片按照一定的比例,改为最小尺寸。

获取真实宽高

我如果拷贝一个千万级像素的大图。面对一堆base64编码,我又如何知道它的宽高?这时你需要构造一个dom。把它放进去。然后趁它加载完成后,拿下来。

所以接下来的做法和上面如出一辙:

const getProps = (img) => (new Promise((resolve, reject) => {
    img.onload = () => {
        resolve({
            width: img.width,
            height: img.height,
            rate: 0.01 * Math.round(100 * img.width / img.height)
        })
    }
}));
// 调用
let base64 = await getBase64();
let img = new Image();
img.src = base64;

// 计算比例 根据 宽/高 校正大小
const { rate, width, height } = await getProps(img);

很简单,很简单。

压缩

压缩的业务需求是这样的:

给定一个宽高极限maxWidthmaxHeight,如果图片没超过这个极限,就用原图无所谓。但超过了。就得执行一系列计算。

// 初始化canvas
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');

// canvas宽高配置。可以从其他文件引入
const canvasConfig = {
    maxWidth: 1200,
    maxHeight: 600
}
const { maxWidth, maxHeight } = canvasConfig;
const canvas_rate = canvasConfig.maxWidth / canvasConfig.maxHeight;

// 解比例
if(width<maxWidth&&height<maxHeight){
    canvas.width = width;
    canvas.height = height;
}else{
    canvas.width = rate > canvas_rate ? maxWidth : maxHeight * rate;
    canvas.height = rate > canvas_rate ? maxWidth / rate : maxHeight;
}

// 核心JS就这个
context.drawImage(img, 0, 0, canvas.width, canvas.height);
let dataURL=canvas.toDataURL('image/jpeg');

这个计算就是解比例。对笔者来说小学六年级难度。听说现在小孩小学二年级奥数就学了。

进入上传流程

canvas最后给到你的也是base64。但传一个blob对后端来说是很友的选择。因此把它转化为blob对象吧

// 转换base64为二进制文件
function dataURLtoBlob(data) {
    var tmp = data.split(',');
    tmp[1] = tmp[1].replace(/\s/g,'');
    var binary = atob(tmp[1]);
    var array = [];
    for(var i = 0; i < binary.length; i++) {
        array.push(binary.charCodeAt(i));
    }
    return new Blob([new Uint8Array(array)], {type: 'image/jpeg'});
}

//上传
let blob=dataURLtoBlob(dataURL);
let fd = new FormData();
fd.append("file", blob);
let res = await this.$post(this.$API.uploadImg, fd});

// res...balabala

你会发现这个dataURLtoBlob和我代码风格不大一样。因为是我复制来的。涉及的非前端api,看不来,这回就当一回API调用工程师吧。

加水印

实际上你可以告诉用户,你的图片是有版权的。说白了也就是加水印。

// 添加水印
context.font = "28px Georgia";
context.fillText('♥️一粒小麦 djtao.net', 30, 60);

可以看到效果:

详情将在canvas一文中讲述。

后端处理

如果直接发送到七牛。那后端配合的就是发送一个token。做的事情简单的令人发指。

七牛有一个nodejs的token生成器。你要做的,就是在进入页面时,请求token生成器。

sudo npm install qiniu -s
// 主要是图片上传
const qiniu = require('qiniu')
export const getQiniuToken = async (ctx, next) => {
      // ak和sk可以在七牛个人中心拿
    var accessKey = '。。。';
    var secretKey = '。。。';
    var mac = new qiniu.auth.digest.Mac(accessKey, secretKey);
    var options = {
        scope: 'storage',//你的空间名
    };
    var putPolicy = new qiniu.rs.PutPolicy(options);
    // 上传凭证
    var uploadToken = putPolicy.uploadToken(mac);
    ctx.body = setResponseData(SUCCESS_CODE, SUCCESS_MSG, uploadToken);
}

看到进入页面,就直接拿到token了。

上传

所以直接回到前端。

//上传
let blob = dataURLtoBlob(dataURL);
let fd = new FormData();
fd.append("file", blob);
fd.append('token',this.token)
let res = await axios.post('http://up-z2.qiniup.com/', fd);
let url='http://markdown.djtao.net/'+res.data.key;

你的空间在哪个区域的机房,就post哪个地址。

那么你就拿到url啦!

组装粘贴内容

还记得那串markdown源码吗?组装一下:

const pasteContnet=`![](${url})`

在markdown编辑器里怎么使用?

两行代码解决:

var clipboardData = event.clipboardData || window.clipboardData;
event.target.value=event.target.value+'\n'+pasteContnet;

最后我们来测试一下。

测试图片地址:

原图:

上传后:

其中转换前尺寸3984*3656,大小为2.5M 转换后尺寸为900X600,大小为193k。

需求完成。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-08-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 一Li小麦 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 获取文件对象
  • 图片处理
    • 压缩
      • 获取真实宽高
      • 压缩
      • 进入上传流程
    • 加水印
    • 后端处理
    • 上传
    • 组装粘贴内容
    相关产品与服务
    文件存储
    文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档