使用对象存储,优雅的管理前端切图
发布于 3 年前 作者 jing45 1596 次浏览 来自 分享

背景:

众所周知,小程序主包&分包大小限制(2M),目前很多开发者都为之抓耳挠腮。

然而目前在现在的开发过程中,一张前端切图动辄十多 K, 几十 K,甚至有些 gif 图片上百 K 都是很常见的。

我们从包大小的角度思考,如果空间都拿来存储图片资源,属实是非常浪费的。

相信每个小程序开发者都对此有过思考,也看到有的项目已经用网络图片来解决此问题。但如果只是拷贝 url 拿过来用,使用起来会比较繁琐,维护起来比较麻烦,怎么更优雅的用,更优雅的维护是我们所追求的。

这里利用腾讯云提供的 对象存储服务(COS)& cos-node-js-sdk,讲一下我对于前端切图素材的解决方案。

思路:

在对象存储上,其实是跟电脑的硬盘一样,是有目录结构的。

如图我们以 ui-material 文件夹为根目录,所有的图片资源都放在这个目录里。

而文件目录本身就是树形结构的,放一张图可能更容易理解

所以就有了思考,想能不能把所有图片以 JSON 的方式存储 ? key=>文件名,value=>图片地址

比如我想访问 HP.jpg 这张图片,我希望是这样的访问 Images.HP

而访问 active/IBM.jpg 我希望是 Images.active.IBM

也就是说把文件的 path 以 json 的方式输出就是不是就 ok 了 ~

下面使用 node 实现 ~

安装依赖:

npm install cos-nodejs-sdk-v5 path-parse ramda lodash --save -dev

1.连接 cos :

// index.js
const COS = require("cos-nodejs-sdk-v5");
const PathParse = require("path-parse");
const Ramda = require("ramda");
const Lodash = require("lodash");
const fs = require("fs");

/** 这里是一些对象存储的配置信息,可以在腾讯云控制台中查看 */
const COS_SECRETID = "xxxx填你自己的";
const COS_SECRETKEY = "xxxx填你自己的";
const COS_BUCKET = "log-1255751956";
const COS_REGION = "ap-hongkong";
const COS_ENCODING_TYPE = "url";
/** 访问地址 */
const COS_ACCESS_DOMAIN = "https://log-1255751956.cos.ap-hongkong.myqcloud.com";
/** UI素材资源目录 */
const UI_MATERIAL_PATH = "ui-material";

/** 获取 cos 实例 */
const cos = new COS({
  SecretId: COS_SECRETID,
  SecretKey: COS_SECRETKEY,
});

__2.获取 __ ui-material 文件夹下的__文件列表,通过官方提供的 cos-node-js-sdk __

/** 获取 Bucket 信息
 * 我们只获取 ui-material 这个文件夹下的文件
 * 所以后面后面统一上传素材文件到这个目录下*/
cos.getBucket(
  {
    /**指定存储桶 */
    Bucket: COS_BUCKET,
    /**指定地区 */
    Region: COS_REGION,
    /**指定文件夹 */
    Prefix: `${UI_MATERIAL_PATH}/`,
    /**指定编码方式 */
    EncodingType: COS_ENCODING_TYPE,
  },
  (err, data) => {
    if (!err) {
      const { Contents } = data;
      console.log('Contents==>',Contents);
    } else {
      console.log("getBuket err", err);
    }
  }
);
// 输出结果:
[
  {
    Key: 'ui-material/',
    LastModified: '2021-09-09T06:32:28.000Z',
    ETag: '"d41d8cd98f00b204e9800998ecf8427e"',
    Size: '0',
    Owner: { ID: '1255751956', DisplayName: '1255751956' },
    StorageClass: 'STANDARD'
  },
  {
    Key: 'ui-material/HP.png',
    LastModified: '2021-09-09T15:43:49.000Z',
    ETag: '"4363f2b9df8a0ec2de805ae2938571fb"',
    Size: '10990',
    Owner: { ID: '1255751956', DisplayName: '1255751956' },
    StorageClass: 'STANDARD'
  },
  {
    Key: 'ui-material/active/',
    LastModified: '2021-09-09T09:24:18.000Z',
    ETag: '"d41d8cd98f00b204e9800998ecf8427e"',
    Size: '0',
    Owner: { ID: '1255751956', DisplayName: '1255751956' },
    StorageClass: 'STANDARD'
  },
  {
    Key: 'ui-material/active/IBM.png',
    LastModified: '2021-09-09T15:44:31.000Z',
    ETag: '"b5974b615efa779c702a15316490f464"',
    Size: '3202',
    Owner: { ID: '1255751956', DisplayName: '1255751956' },
    StorageClass: 'STANDARD'
  },
  {
    Key: 'ui-material/active/moduleA/',
    LastModified: '2021-09-09T15:46:04.000Z',
    ETag: '"d41d8cd98f00b204e9800998ecf8427e"',
    Size: '0',
    Owner: { ID: '1255751956', DisplayName: '1255751956' },
    StorageClass: 'STANDARD'
  },
  {
    Key: 'ui-material/active/moduleA/intel.png',
    LastModified: '2021-09-09T15:46:12.000Z',
    ETag: '"edec5fe92c7d52cb69a40890eaa6a113"',
    Size: '5316',
    Owner: { ID: '1255751956', DisplayName: '1255751956' },
    StorageClass: 'STANDARD'
  },
  {
    Key: 'ui-material/order.png',
    LastModified: '2021-09-09T07:18:30.000Z',
    ETag: '"dd5cee76f07bc77f1e3b8b5e65e130da"',
    Size: '3106',
    Owner: { ID: '1255751956', DisplayName: '1255751956' },
    StorageClass: 'STANDARD'
  }
]

我们可以清晰的看见 List 中的 Key 其实就是我们所需要的文件路径,我们只需要去解析这个 Key 就 ok 了

3.获取图片路径:

const Key = 'ui-material/active/moduleA/intel.png';
const url = `${COS_ACCESS_DOMAIN}/${Key}` 
// 结果=====> https://log-1255751956.cos.ap-hongkong.myqcloud.com/ui-material/active/moduleA/intel.png

 

4.获取图片对应的 objcet props:

const parsePath = PathParse(Key);  /** 解析 path 信息 **/
const props = [ ...parsePath.dir.split('/') ,parsePath.name ];
props.shift(); /** 删除数组第一个元素,因为 ui-material 是根目录 ===> [ 'active', 'moduleA', 'intel' ] **/

5.利用 loadsh.set 为对象属性赋值

let obj = {};
Lodash.set(obj, props, url);
console.log(obj)
/** =====> 
{
  active: {
    moduleA: {
      intel: 'https://log-1255751956.cos.ap-hongkong.myqcloud.com/ui-material/active/moduleA/intel.png'
    }
  }
} **/

好了到这里我们以经可以顺利的生成文件的 JSON 了,没错它是一个树形结构。

我们只需要把这个 JSON 给写入 js 文件,后续直接从这个文件拿数据就行了。

6.写入数据到 js 文件

const outputStr = `export default ${Ramda.toString(json)};`;
fs.writeFileSync("./assets/data.js", outputStr);

__7.运行脚本 __

node index.js

会得到这样一个 js 文件

export default {
  HP: "https://log-1255751956.cos.ap-hongkong.myqcloud.com/ui-material/HP.png",
  active: {
    IBM: "https://log-1255751956.cos.ap-hongkong.myqcloud.com/ui-material/active/IBM.png",
    moduleA: {
      intel:
        "https://log-1255751956.cos.ap-hongkong.myqcloud.com/ui-material/active/moduleA/intel.png",
    },
  },
  order:
    "https://log-1255751956.cos.ap-hongkong.myqcloud.com/ui-material/order.png",
};

8.在小程序里使用

// assets/index.js

import Images from './data';
export { Images }

在 Page 里使用

import { Images } from '../../assets/index';

Page({
    data: {
       // 这种方式引用有没有很爽
       intelIcon: Images.active.moduleA.intel
     }
});

可以结合 npm script 或者 gulp 每次在图片更新时,重新执行脚本,更新 data.js

9.完整代码

const COS = require("cos-nodejs-sdk-v5");
const PathParse = require("path-parse");
const Ramda = require("ramda");
const Lodash = require("lodash");
const fs = require("fs");

/** 这里是一些对象存储的配置信息,可以在腾讯云控制台中查看到 */
const COS_SECRETID = "xxxx填你自己的";
const COS_SECRETKEY = "xxxx填你自己的";
const COS_BUCKET = "log-1255751956";
const COS_REGION = "ap-hongkong";
const COS_ENCODING_TYPE = "url";
/** 访问地址 */
const COS_ACCESS_DOMAIN = "https://log-1255751956.cos.ap-hongkong.myqcloud.com";
/** UI素材资源目录 */
const UI_MATERIAL_PATH = "ui-material";

/** 获取 cos 实例 */
const cos = new COS({
  SecretId: COS_SECRETID,
  SecretKey: COS_SECRETKEY,
});

/** 获取 Bucket 信息
 * 我们只获取 ui-material 这个文件夹下的文件
 * 所以后面后面统一上传素材文件到这个目录下*/
cos.getBucket(
  {
    /**指定存储桶 */
    Bucket: COS_BUCKET,
    /**指定地区 */
    Region: COS_REGION,
    /**指定文件夹 */
    Prefix: `${UI_MATERIAL_PATH}/`,
    /**指定编码方式 */
    EncodingType: COS_ENCODING_TYPE,
  },
  (err, data) => {
    if (!err) {
      const { Contents } = data;
      const effectiveFile = filterEffectiveFile(Contents);
      const jsonObj = genJson(effectiveFile);
      outJsFile(jsonObj);
    } else {
      console.log("getBuket err", err);
    }
  }
);

/** 输出 js 文件*/
function outJsFile(json) {
  try {
    const outputStr = `export default ${Ramda.toString(json)};`;
    fs.writeFileSync("./assets/data.js", outputStr);
  } catch (e) {
    console.log("writeFile file fail", e);
  }
}

/** 过滤出有效的 file */
function filterEffectiveFile(Contents) {
  /**过滤出 size > 0 的file */
  return Contents.filter((_) => _.Size > 0).map((_) => {
    const url = `${COS_ACCESS_DOMAIN}/${_.Key}`;
    const parsePath = PathParse(_.Key);
    const props = [...parsePath.dir.split("/"), parsePath.name];
    props.shift();
    return {
      url,
      props,
    };
  });
}
/** 生成 json 对象 */
function genJson(fileList) {
  let objects = {};
  fileList.forEach((file) => {
    /** 核心方法 可以看 loadsh 文档中的 set 方法 */
    Lodash.set(objects, file.props, file.url);
  });
  return objects;
}
回到顶部