背景
目前有部分前端项目附件会保存在阿里云上管理,无论是前端直接调用ali-oss上传,还是通过调用后端接口上传,最终返回的是一个oss
图片链接,基于不同的需求场景我们需要对返回的图片链接进行相应地处理,虽然ali-oss
提供了一套较为完善的开发文档,但是实际使用起来还是会有点不太方便。
同学们对以下图片进行缩放处理的操作有点眼熟吗?
p3-juejin.byteimg.com/tos-cn-i-k3…
上述图片的处理是直接在原图片链接添加不同的参数来完成,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
就能获取链接。下面对joinParams
和decamelize
两个方法说明一下。
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>
效果如下:
使用过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
}
最后
感谢大家的细心阅读,现在是不是跃跃欲试地想运用到自己的项目中了,如果这篇文档对同学们有所帮忙和启发,欢迎点赞收藏。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!