最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • OSS 图片链接处理工具和组件

    正文概述 掘金(ZCoder)   2021-02-10   842

    背景

    目前有部分前端项目附件会保存在阿里云上管理,无论是前端直接调用ali-oss上传,还是通过调用后端接口上传,最终返回的是一个oss 图片链接,基于不同的需求场景我们需要对返回的图片链接进行相应地处理,虽然ali-oss提供了一套较为完善的开发文档,但是实际使用起来还是会有点不太方便。

    同学们对以下图片进行缩放处理的操作有点眼熟吗?

    p3-juejin.byteimg.com/tos-cn-i-k3…

    OSS 图片链接处理工具和组件

    上述图片的处理是直接在原图片链接添加不同的参数来完成,x-oss-process=image 指定对图片类型的处理,resize指定当前为缩放操作,w_200 则缩放图片宽度为200px。

    使用过oss 图片链接的同学会发现,这个图片处理方式只是冰山一角,例如还有对图片质量的控制,模糊度处理等,下面列出几种处理图片的方式

    • 缩放:image-demo.oss-cn-hangzhou.aliyuncs.com/example.jpg…
    • 模糊:image-demo.oss-cn-hangzhou.aliyuncs.com/example.jpg…
    • 质量变换:image-demo.oss-cn-hangzhou.aliyuncs.com/example.jpg…

    细心的同学可能已经发现了规律,对于图片处理x-oss-process=image是固定的,各种处理类型都是都是使用 '/' 分割,第一个参数是处理类型值,如resize,然后后面紧接一个','分割,后面的参数则是使用k_v的格式进行分割,但又几个类型方式不同,他们只需要传v,例如rotate等。

    竟然我们已经早到了规律之后,我们有没有更好的方式根据指定的配置方式对链接进行处理呢?其实我们可以使用对象的方式描述我们的处理方式,可以链式调用以多种不同方式处理链接。

    代码实现

    声明接口和类

    根据ali-oss官网文档的API说明,我们先定义好各种处理方式的参数接口,以下以定义缩放的接口为例,其他的方式以此类推。

    export type MethodsType =
      | 'resize'
      | 'blur'
      | 'circle'
      | 'crop'
      | 'indexcrop'
      | 'rotate'
      | 'bright'
      | 'contrast'
      | 'sharpen'
      | 'format'
      | 'watermark'
      | 'interlace'
      | 'quality'
      | 'roundedCorners'
      | 'autoOrient'
    
    type OssResizeMode = 'lfit' | 'mfit' | 'fill' | 'pad' | 'fixed'
    
    export interface OssResize {
      /**
       * 指定缩放的模式
       */
      m?: OssResizeMode
      /**
       * 指定目标缩放图的宽度
       * - [1,4096]
       */
      w?: number
      /**
       * 指定目标缩放图的高度。
       * - [1,4096]
       */
      h?: number
      /**
       * 指定目标缩放图的最长边。
       * - [1,4096]
       */
      l?: number
      /**
       * 指定目标缩放图的最长边。
       * - [1,4096]
       */
      s?: number
      /**
       * 指定当目标缩放图大于原图时是否进行缩放。
       * - 1(默认值):表示不按指定参数进行缩放,直接返回原图。
       * - 0:按指定参数进行缩放。
       */
      limit?: 0 | 1
      /**
       * 当缩放模式选择为pad(缩放填充)时,可以设置填充的颜色
       * - RGB颜色值,例如:000000表示黑色,FFFFFF表示白色。
       * - 默认值:FFFFFF(白色)
       */
      color?: string
    }
    

    另外对处理函数的接口我们也需要声明一下。

    export interface ApiHandler<T> {
      (params: T): OssImage
    }
    

    接下来我们看下处理图片类的定义

    class OssImage {
      private methods = [
        'resize',
        ...
      ]
      ...
      resize!: ApiHandler<OssResize>
      ...
    }
    

    划重点了,我们要遍历methods数组的字符串来定义不同的图片处理方法

    ...
    /**
     * 处理的方法参数对象
     */
    private methodKeys: Record<string, any> = {}
    
    constructor(private originUrl: string) {
      this.originUrl = originUrl
    
      this.methods.forEach((method: MethodsType) => {
        OssImage.prototype[method] = function(
         key: any,
         value?: any,
        ) {
          if (typeof key === 'object') {
            this.methodKeys[method] = key
          } else {
            if (!this.methodKeys[method]) this.methodKeys[method] = {}
            this.methodKeys[method][key] = value
          }
          return this
        }
      })
    }
    ...
    

    构造函数中的originUrl参数为原始oss 图片链接,我们遍历methods数组,将方法设置到类的原型对象prototype上,如果我们只是单纯的改变图片的一个维度的参数是,可以传入两个参数,第一个参数为k,第二个参数为v;如果是多个维度是,则只需要传入第一个参数,而且为k-v键值对对象;还有一种情况就是第一个参数是一个v的,例如rotate类型等;最终需要返回当前实例this,便于使用链式方式对图片以多个不同类型进行处理,例如:

    // 链式调用,缩放图片宽度为100px,高度为100px,输出质量为50,旋转180度
    const ossImage = new OssImage(url).resize({w: 100, h: 100}).quality({ q: 50 }).rotate(180)
    

    接着只需要把各种类型的处理方式拼接起来,返回处理好的图片链接,该功能就完成了

    ...
     /**
      * 获取处理后的链接
      */
    get url() {
      const url = new URL(this.originUrl)
      const formatParams = Object.keys(this.methodKeys).reduce((acc, method) => {
        const str = joinParams(this.methodKeys[method], '_', ',')
        if (!str) return acc
        return (acc += `/${decamelize(method)},${str}`)
      }, '')
      url.searchParams.set('x-oss-process', `image${formatParams}`)
      return url.href
    }
    ...
    

    这里使用getter方式,直接访问实例的url就能获取链接。下面对joinParamsdecamelize两个方法说明一下。

    joinParams方法的作用的是拼接每个处理方式的配置信息,例如joinParams({w:100, h: 100}, '_', ','),返回的就是'w_100,h_100',具体实现如下:

    /**
     * 用joinParams拼接obj里面的每对k-v,并用splitFlag分割
     */
    function joinParams(obj: Record<string, any>, joinFlag: string, splitFlag: string): string {
      return Object.keys(obj)
        .reduce((acc, cur) => {
          if (obj[cur]) return (acc += `${splitFlag}${cur}${joinFlag}${obj[cur]}`)
          return (acc += `${splitFlag}${cur}`)
        }, '')
        .slice(1)
    }
    

    decamelize方法则是将处理类型驼峰转中横线链接,例如decamelize('autoOrient'),返回的就是'auto-orient',具体实现如下:

    /**
     * 驼峰转中横线
     */
    export function decamelize(str: string): string {
      return str.replace(/\B([A-Z])/g, '-$1').toLowerCase()
    }
    

    完成上述功能后,我们就可以拿到处理好的图片链接了

    const ossImage = new OssImage(url)
    // 获取处理好的图片链接
    const src = ossImage.resize({w: 100, h: 100}).quality({ q: 50 }).url
    

    继续优化下,如果我们每次都需要使用new 的方式对图片处理是不是很麻烦,我们可不可以不使用new 方式而是直接调用ossImage方法进行处理呢?答案是可以的,只需要用一个函数处理返回就好。

    export function ossImage(url: string): OssImage {
      return new OssImage(url)
    }
    

    接下来就可以使用以下方式调用了。

    // 获取处理好的图片链接
    const src = ossImage(url).resize({w: 100, h: 100}).quality({ q: 50 }).url
    

    继续思考下,如果是在Vue项目中,可以不可以使用<oss-image src="..." :resize="..."></oss-image>方式进行调用呢?答案是可以的,只需要基于工具集成到Vue组件就OK了。

    封装成Vue组件

    为了方便说明,假设基于ElementUI开发,我们使用使用ElImage组件进行二次封装,这里直接用img标签也可以实现,只是需要额外编写下样式和交互效果。具体实现如下:

    <template>
      <el-image :src="url" v-bind="$attrs" v-on="$listeners"></el-image>
    </template>
    
    <script lang="ts">
    import { Component, Vue, Prop } from 'vue-property-decorator'
    import ossImage from 'path/to/oss-image'
    import { OssResize, ... } from 'path/to/oss-image/interface'
    
    const METHOD_LIST = ['quality', 'resize', ...]
    
    @Component({
      name: 'OssImage',
    })
    export default class OssImage extends Vue {
      @Prop({ type: String, required: true }) src!: string
    
      @Prop() resize!: OssResize
      // 其他配置项以此类推...
      ...
    
      get url() {
        let target = ossImage(this.src)
        METHOD_LIST.forEach((key: MethodsType) => {
          this[key] && (target = (target[key] as any)(this[key]))
        })
        return target.url
      }
    }
    </script>
    

    是不是很简单,就是一堆props和一个computed就完成了,接下来我们就可以愉快的使用这个组件了。

    <template>
      <div>
        <oss-image :src="url" :resize="resize"></oss-image>
        <oss-image :src="url" :resize="resize" :quality="quality"></oss-image>
        <oss-image :src="url" :resize="resize" :blur="blur"></oss-image>
      </div>
    </template>
    
    <script lang="ts">
    export default {
      data() {
        return {
          url:'https://xiaoedu-ams.oss-cn-shenzhen.aliyuncs.com/test/20201029/18/1603966544271Fiine.jpeg',
          resize: { w: 160, h: 160 },
          quality: { q: 10},
          blur: { r: 25, s: 10 },
        }
      },
    }
    </script>
    

    效果如下:

    OSS 图片链接处理工具和组件

    使用过el-image组件的同学应该都知道,其实它是支持预览功能的,而且它提供了preview-src-list属性来表示要预览的图片列表,会根据当前url链接判断当前预览的是第几张图片,如果我们处理过url,那么无论我们点击的是第二张图片,那我们预览时也会跳到第一张,所以我们重写了它的imageIndex的计算属性。

    import Element from 'element-ui'
    
    /**
     * 兼容oss-image 阿里云图片 预览展示
     */
    Element.Image.computed.imageIndex = function() {
      let previewIndex = 0
      const srcIndex = this.previewSrcList.findIndex((url: string) =>
        this.src.includes(url),
      )
      if (srcIndex >= 0) {
        previewIndex = srcIndex
      }
      return previewIndex
    }
    

    最后

    感谢大家的细心阅读,现在是不是跃跃欲试地想运用到自己的项目中了,如果这篇文档对同学们有所帮忙和启发,欢迎点赞收藏。


    起源地下载网 » OSS 图片链接处理工具和组件

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元