资讯专栏INFORMATION COLUMN

开发一个本地上传图片控件你需要知道的知识点

qieangel2013 / 966人阅读

摘要:必须阻止和事件的默认行为,这样才能触发事件文件对象将图片转换为获取到图片内容微信截图上传监听事件,通过剪贴板对象获取图片信息。但是最好的方案还是不管下载上传都直接走独立图片服务器,避免对服务器造成额外的压力。

更多文章请点击Jade

接了一个「常规」需求:开发一个本地上传图片控件,需要支持三种上传方式:

支持打开本地目录,选择本地图片上传

支持拖曳图片上传

支持微信截图上传

我们先从工程角度来看一下用户上传图片的流程是怎样子的:

用户选择了一张本地图片,或者拖曳了一张图片,或者通过微信截图了一张图片。这时,我们需要知道用户所选择图片的信息,比如图片的内容、图片的大小、图片的类型等。

用户上传图片。这时,我们需要保存图片。

用户查看图片。这时,我们需要把第2步中保存的图片展示给用户。

如何获取到图片信息 通过input 打开本地目录,选择本地图片上传

通过type为file的input标签选择本地图片上传,每次选择不同图片的时候会触发onChange事件。为什么我要强调不同图片,因为当两次选择的图片是一样的情况下,onChange事件无法触发。解决方法:每次处理完后手动置空input的value。

  
  // 置空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对象获取图片信息。

  
Drop Zone
  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服务器造成额外的压力。

后续,探究图片服务器架构 & 如何应对大文件的上传 & Blob Buffer Stream三者之间的关系。 本文参考资料:

理解DOMString、Document、FormData、Blob、File、ArrayBuffer数据类型

复制粘贴的高级玩法

使用FileReader.readAsArrayBuffer()在浏览器中处理大文件

大型网站图片服务器架构的演进

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/90784.html

相关文章

  • 精彩文章大合集- 收藏集 - 掘金

    摘要:发布应用市场的平台抢红包工具红包精灵开源啦掘金红包精灵,如果喜欢,点个开源不易。作者将原素材文章进行了新内容的添加和重新排列,但是因为文章高效的代码编写技巧总结前端掘金本文总结了代码编写技巧,来提升你的和代码。 收藏安卓开发中非常实用优秀的库! 有图有真相! - Android - 掘金本来是打算收藏工具类的,但转念一想,已经有这么多优秀的库了,就没必要再去重复造轮子了,便归纳工作中比...

    ermaoL 评论0 收藏0
  • 从前端界面开发谈微信小程序体验

    摘要:在开发之前你要有微信开发者工具。同时为了更适合开发微信小程序,还对进行了扩充以及修改,直接帮我们把适配的一部分工作都做了,比如他的,可以根据屏幕宽度进行自适应,规定屏幕宽为。 本文由云+社区发表 这段时间有幸加入了一个关于微信小程序的项目开发组,从无到有的根据文档自行学习了小程序的开发过程,前面已经有几位前辈的文章珠玉在前,我这里就先从前端界面的开发方面谈一谈小程序以及我所遇到的问题吧...

    hellowoody 评论0 收藏0
  • 上传图片本地预览效果实现(兼容IE8)

    摘要:以上传图片为例。我们可以通过获取上传的图片相关信息,但是想要实现本地预览还需要借助来实现可以读取本地图片,并将图片数据转换成编码的字符串形式嵌入到页面中。在我们实现上传图片的效果里,就有用到。图片预览兼容处理及以下版本不支持和。 最近项目里需要用到上传图片并预览的功能,于是写了个jQuery预览图片插件,下载地址。如果有需要的,可以直接下载。第一次写jQuery插件,如有不对之处,欢迎...

    avwu 评论0 收藏0
  • JavaScript精编干货

    摘要:老姚浅谈怎么学鉴于时不时,有同学私信问我老姚,下同怎么学前端的问题。撸码听歌,全局控制。 浅析用 js 解析 xml 的方法 由于项目上需要解析 xml,于是各种百度,然后自己总结了下各个主流浏览器解析 xml 的方法,只能是很浅显的知道他的用法,但是还没有深层次的研究。 装 X - 建立自己的斗图网站库 之前加过一个斗图群,看到很多经典的表情,然后就收藏到了 QQ, 迫于本屌丝开不起...

    Fourierr 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<