摘要:接了一个「常规」需求:开发一个本地上传图片控件,需要支持三种上传方式: 支持打开本地目录,选择本地图片上传 支持拖曳图片上传 支持微信截图上传 我们先从工程角度来看一下用户上传图片的流程是怎样子的: 用户选择了一张本地图片,或者拖曳了一张图片...
接了一个「常规」需求:开发一个本地上传图片控件,需要支持三种上传方式:
支持打开本地目录,选择本地图片上传 支持拖曳图片上传 支持微信截图上传我们先从工程角度来看一下用户上传图片的流程是怎样子的:
用户选择了一张本地图片,或者拖曳了一张图片,或者通过微信截图了一张图片。这时,我们需要知道用户所选择图片的信息,比如图片的内容、图片的大小、图片的类型等。 用户上传图片。这时,我们需要保存图片。 用户查看图片。这时,我们需要把第2步中保存的图片展示给用户。如何获取到图片信息
通过input 打开本地目录,选择本地图片上传
通过type为file的input标签选择本地图片上传,每次选择不同图片的时候会触发onChange事件。为什么我要强调不同图片,因为当两次选择的图片是一样的情况下,onChange事件无法触发。解决方法:每次处理完后手动置空input的value。
<input id=img-input type=file accept=image/* onChange={this.bindChooseEvents} /> // 置空input value clearImgInputValue () { document.getElementById(img-input).value = } bindChooseEvents = (e) => { console.log(choose a image) // 获取File对象 const file = e.target.files[0] let size = file.size if (!file.type.match(image.*)) { AntMessage.warning(File\s type is not supported. Images only.) this.clearImgInputValue() return; } if (size > maxSize) { AntMessage.warning(The size of the image is too large) this.clearImgInputValue() return; } /* eslint-disable */ const reader = new FileReader() // FileReader /* eslint-disable */ // 将图片转换为base64 reader.readAsDataURL(file) reader.onload = (arg) => { // 获取到base64图片内容 const fileStream = arg.target.result /** * overwrite do something * */ this.clearImgInputValue() } }拖曳图片上传
监听拖曳事件,通过拖曳相关的DataTransfer对象获取图片信息。
<div id=drop-zone style={{width: 100px, height: 100px}}>Drop Zone</div> bindDragEvents = (e) => { const handleDragOver = (event) => { event.stopPropagation() event.preventDefault() event.dataTransfer.dropEffect = copy } // 必须阻止dragenter和dragover事件的默认行为,这样才能触发 drop 事件 const handleFileSelect = (event) => { event.stopPropagation() event.preventDefault() const files = event.dataTransfer.files // 文件对象 const file = files[0] const size = file.size const type = file.type if (!type.match(image.*)) { AntMessage.warning(File\s type is not supported. Images only.) return; } if (size > maxSize) { AntMessage.warning(The size of the image is too large) return; } /* eslint-disable */ const reader = new FileReader() /* eslint-disable */ // 将图片转换为base64 reader.readAsDataURL(file) reader.onload = (arg) => { // 获取到base64图片内容 const fileStream = arg.target.result /** * overwrite do something * */ } } const dropZone = document.getElementById(drop-zone); dropZone.addEventListener(dragover, handleDragOver, false); dropZone.addEventListener(drop, handleFileSelect, false); }微信截图上传
监听paste事件,通过剪贴板对象clipboardData获取图片信息。
bindClipEvents() { document.addEventListener(paste, (e) => { console.log(paste a image) const clipboard = e.clipboardData // 有无内容 if (!clipboard.items || !clipboard.items.length) { AntMessage.warning(No content in the clipboard) return; } let item = clipboard.items[0] if (item.kind === file && item.type.match(image.*)) { // 获取图片文件 let imgFile = item.getAsFile() if (imgFile.size > maxSize) { AntMessage.warning(The size of the image is too large) return; } const reader = new FileReader() // 将图片转换为base64 reader.readAsDataURL(imgFile) reader.onload = (arg) => { // 获取到base64图片内容 const fileStream = arg.target.result /** * overwrite do something * */ } } else { AntMessage.warning(File\s type is not supported. Images only.) } }, false) }如何保存
图片保存一般都采用独立图片独立域名服务器,不会傻乎乎地把图片保存在web服务器上也不会直接存在项目表的数据库中。这样做有什么好处呢?
图片访问是I/O密集型操作,很消耗服务器资源,从Web服务器独立出来后,能够减少Web服务器压力 便于扩容、容灾和数据迁移 浏览器有同域名下的并发策略限制 请求图片一般并不需要cookie,但是浏览器发起的所有同域名请求时,http头部都会自动带上cookie信息,导致浪费带宽 方便对图片访问做负载均衡,可以对图片应用各种缓存策略 方便迁移CDN ...总结&优化
流程图:
不足:
项目实践中我们虽然采用了独立图片服务器,下载过程只是通过Web服务器去数据库拿到图片地址,但是我们的上传操作仍旧经过了Web服务器,需要Web服务器上的应用程序来处理,所以上传过程仍旧对Web服务器造成压力。所幸的是,我们对图片上传大小进行了1M的大小限制,同时作为内部系统没啥访问压力,而且图片上传功能也并不是很高发的行为,所以这么做基本也不会有啥问题。但是最好的方案还是不管下载上传都直接走独立图片服务器,避免对Web服务器造成额外的压力。
版权声明:除特别声明外,本站所有文章皆是来自互联网,转载请以超链接形式注明出处!