likes
comments
collection
share

客户端优化(小程序+后台) | 【图片优化】(二)

作者站长头像
站长
· 阅读数 35

前言

一名正在自由职业的程序员的独立开发之路 每天100元的图片流量费用 逼得我不得不做图片优化 目前已经全部优化完成 希望我的经历能给你带来帮助 这是图片优化的第二篇

说明

我的项目主要客户端主要包括 小程序后台管理

图片优化系列文章

小程序优化

1.不怎么变化的图片全部存放到本地

很多图片都是通过网络下发的,这样确实方便灵活。

但是目前业务已经稳定,不会有太大的变化

像图标、banner图,这些基本不会太有变化,那么就直接放到小程序里面,直接加载本地资源

当然小程序的包大小也要控制

如果压缩后的体积大于2M,那么会不给上传,再做一定的取舍

当小程序上传包太大,点击可以看得到每一块模块的大小,占的比例大小,帮助你分析

另外我也采取了分包的策略

2.图片全部都通过工具优化一遍

这个可以通过网上的工具,也可以通过自己代码去压缩

当然压缩完之后,至少要保证不至于太糊

经过摸索,我这边基本上 乘以 0.3 的系数是最好的

3.网络图片加载完,下载到本地

小程序这边我做一个图片加载的组件

在得到图片的URL的时候,直接去本地存储中去找,之前有没有下载过

如果下载过了,直接拿上次下载的用

如果没有下载过,那么直接下载下来,用URL作为Key

直接上代码了

# 组件的html
<image class="ff-class" src='{{path}}' lazy-load="true" mode="{{mode}}" bindload="{{bindload}}" binderror="{{binderror}}" />

#组件的js
const app = getApp()
const fileCache = require('../../utils/fileCache')
console.log('fileCache ===',fileCache)
Component({
	externalClasses: ['ff-class'],
	/**
	 * 组件的属性列表
	 */
	properties: {
		mode: {
			type: String,
			default: '',
		},
		src:{
			type:String,
			default: '',
			observer:function(newVal){
				this.initUrl()
			  }
			}
	},
	/**
	 * 组件的初始数据
	 */
	data: {
		ffmode:'scaleToFill',
		path:'',
	},
	lifetimes: {
		attached() {
			this.initUrl()
		},
	},
	/**
	 * 组件的方法列表
	 */
	methods: {
		initUrl(){
			// console.log('initUrl ======')
			let {src,mode} = this.properties
			console.log('mode ===',mode)
			if(!/^https/.test(src)) src = src.replace('http','https')
			let path = fileCache.getStorageImage(src)
			this.setData({path})
			if(mode) this.setData({ffmode:mode})
			console.log('path ====',path)
		},
		binderror(error){
			console.log('error ====',error)
		},
		bindload(res){
			console.log('加载图片成功 ====',error)
		}
	}
})

# 下载的逻辑
# fileCache.js

const fileSystem = wx.getFileSystemManager()
const getStorageImage = (web_image) => {
  let webImages = wx.getStorageSync('webImages') || []
  let webImage = webImages.find(y => y.web_path === web_image)
  if (webImage) {
    try {
      fileSystem.accessSync(webImage.local_path)
      return webImage.local_path
    } catch(e) { 
      let webImageIdx = webImages.findIndex(y => y.web_path === web_image)
      webImages.splice(webImageIdx, 1)
      wx.setStorageSync('webImages', webImages)
    }
  } else {
    wx.downloadFile({
      url: web_image,
      success : res =>{
        if (res.statusCode === 200) {
          let filePath = res.tempFilePath
          let webImageStorage = wx.getStorageSync('webImages') || []
          let storage = {
            web_path: web_image,
            local_path: filePath,
            last_time: Date.parse(new Date()),
          }
          webImageStorage.push(storage)
          wx.setStorageSync('webImages', webImageStorage)
        }
      },
      fail: err=>{
        console.log('downLoad 报错了',err)
      }
    })
  }
  return web_image
}

module.exports = {
  getStorageImage
}


管理后台优化(Vue)

管理后台主要功能是上传图片,所以压缩图片才是最大的优化

压缩图片

压缩图片的方法为getZipFile

通过getUrlByFile方法获取图片的 URL

  1. 如果文件的大小乘以 0.3 之后还是大于 60KB,那么就调用 startZipToSize 这个方法是把图片压缩到固定的大小内 里面主要通过二分法不断的去压缩图片,最后也有可能不能达到60KB

  2. 如果文件大小乘以 0.3小于60KB,那么就调用 startZipQuality,这个方法就是按照 0.3 的系数去压缩图片

// v2.0.8 压缩图片
const getZipFile = async ({file,url})=>{
  // 通过file 得到url
  // 判断如果 file 的size * 0.3 > 60 
  let {original_url } = await getUrlByFile({file})
  if((file/1024)*0.3 >= 60){
    const {zip_file} = await startZipToSize({original_url,fileName:file.name})
    if(!zip_file) return alert('getZipFile = startZipToSize =获取zip_file 失败')
    return zip_file
  }else{
    const {zip_file} = await startZipQuality({original_url,fileName:file.name})
    if(!zip_file) return alert('getZipFile = startZipQuality =获取zip_file 失败')
    return zip_file
  }
}


# 通过文件获取URL
const getUrlByFile = ({file})=>{
  if(!file)return alert('getUrlByFile 中file参数不存在')
  if(!window.FileReader) return alert('请更换浏览器,此浏览器不支持 FileReader')
  return new Promise((resolve,reject)=>{
    let reader = new FileReader()
    reader.onload = (event)=>{
        let original_url = event.target.result
        console.log('通过 file 后的 image 的url 的地址为 ...',original_url)
        resolve({original_url})
        // original_file = e.target.files[0]
    }
    reader.readAsDataURL(file)
  })
}
/**
 * 将图片压缩到 一定的比例之内(默认是0.3)
 * @param {*} param0 
 * @returns 
 */
const startZipQuality = ({quality = 0.3,original_url='',fileName=''})=>{ 
  if(/^http/.test(original_url)) 
  if(!fileName) return alert('startZipQuality == fileName 不存在')
  console.log('fileName ===',fileName)
  return new Promise((reslove,reject)=>{
    var image = new Image() // 创建 img 元素
    image.onload = async()=>{
        var canvasUrl,miniFile;
        const canvas = document.createElement('canvas')
        canvas.width = image.width
        canvas.height = image.height
        canvas.getContext('2d').drawImage(image,0,0,image.width,image.height) // 绘制canvas
        canvasUrl = canvas.toDataURL('image/jpeg',quality )
        const buffer = atob(canvasUrl.split(',')[1])
        let length = buffer.length
        const bufferArray = new Uint8Array(new ArrayBuffer(length))
        while(length--){
            bufferArray[length] = buffer.charCodeAt(length)
        }
        miniFile = new File([bufferArray], fileName, { type: 'image/jpeg' })
        let zip_url =  await getUrlByFile({file:miniFile})
        let zip_file = miniFile
        reslove({zip_file,zip_url,zip_img:image})
    }
    image.setAttribute('crossOrigin', 'anonymous') //关键
    image.src = /^http/.test(original_url) ? `${original_url}?time=${new Date().valueOf()}` : original_url
  })
}
/**
 * 压缩到一定的体积内 默认是60K
 * @param {*} param0 
 */
const startZipToSize = ({limitSize = 60 ,original_url='',fileName=''})=>{ 
  if(/^http/.test(original_url)) 
  if(!fileName) return alert('startZipToSize == fileName 不存在')
  return new Promise((reslove,reject)=>{
    var image = new Image() 
    image.onload = async()=>{
        var canvasUrl,miniFile;
        const canvas = document.createElement('canvas')
        canvas.width = image.width
        canvas.height = image.height
        canvas.getContext('2d').drawImage(image,0,0,image.width,image.height) // 绘制canvas
        let L = true // 判断是左侧还是右侧 二分法
        let quality = 0
        let start = 0,end = 1;  
        let count = 0;
        let lastSize = 0;
        while(count <= 2){
            quality = (start+end)/2
            canvasUrl = canvas.toDataURL('image/jpeg',quality )
            const buffer = atob(canvasUrl.split(',')[1])
            let length = buffer.length
            const bufferArray = new Uint8Array(new ArrayBuffer(length))
            while(length--){
                bufferArray[length] = buffer.charCodeAt(length)
            }
            miniFile = new File([bufferArray], fileName, { type: 'image/jpeg' })
            if(lastSize === miniFile.size){
                console.log(`法压缩到指定大小,最小为${lastSize/1024}KB`)
                // alert(`法压缩到指定大小,最小为${lastSize/1024}KB`)
                break;
            }
            lastSize = miniFile.size
            if(miniFile.size/1024 > limitSize ){
                end = quality
            }else{
                start = quality
                count++
            }
        }
        let url = await getUrlByFile({file:miniFile})
        let zip_url = url
        let zip_file = miniFile
        reslove({zip_file,zip_url,zip_img:image})
    }
    image.setAttribute('crossOrigin', 'anonymous') //关键
    image.src = /^http/.test(original_url) ? `${original_url}?time=${new Date().valueOf()}` : original_url  
  })
}

上传

由于我已经做了自己的图片服务器,所以此处的删除直接传到我自己的服务器

//zip_file 图片文件 prefix 图片命名的前缀,可以不穿
const submitImg = ({zip_file,prefix=""})=>{
  if(!zip_file) return alert('submitImg 中不存在zip_file')
  console.log('submitImg === zip_file',zip_file)
  return new Promise(async (resolve,reject)=>{
    const data = new FormData();
      data.set('file', zip_file);
      if(prefix) data.set('prefix', prefix);
      let requestUrl = /^https/.test(window.location.href) ? 'https://abc/upload' : 'http://abc/upload'
      const res = await axios.post(requestUrl, data, {
          headers: { 'content-type': 'multipart/form-data' }
      });
      let backData = res.data
      if(backData && backData.code === 200 && backData.data && backData.data.url){
        // console.log('-----上传成功',zip_file.name)
        resolve({upload_url:backData.data.url})
      }else{
        // console.log('-----上传失败',zip_file.name)
        reject()
      }
      console.log(res);    
  })
}

总结

小程序和管理后台优化起来还是比较简单

一个是根据业务来优化,存本地,节省网络请求流量

一个是上传图片做压缩

这是一个【图片优化】系列文章的【第二篇】,正在更新

转载自:https://juejin.cn/post/7242550697521594428
评论
请登录