发布一个验证图片格式的npm包

130 阅读2分钟

验证图片格式

方式一

后缀

function isValidImage(filename) {
  // 正则表达式匹配常见的图片格式
  const imageRegex = /.(jpg|jpeg|png|gif|bmp|webp)$/;
  return imageRegex.test(filename.toLowerCase());
}

方式二

file.type

方式三

根据文件头信息判断

支持的图片格式
const TYPES = {
  JPG: "JPG",
  JPEG: "JPEG",
  PNG: "PNG",
  GIF: "GIF",
  WEBP: "WEBP",
  ICO: "ICO",
  BMP: "BMP",
};
文件头信息
const FILE_HEADER_INFO = {
  [TYPES.JPG]: {
    headerLength: 3, // 代表图片格式的文件头信息长度
    hexInfo: "FF D8 FF", // 十六进制图片格式文件头信息
  },
  [TYPES.JPEG]: {
    headerLength: 3,
    hexInfo: "FF D8 FF",
  },
  [TYPES.PNG]: {
    headerLength: 4,
    hexInfo: "89 50 4E 47",
  },
  [TYPES.GIF]: {
    headerLength: 6,
    hexInfo: ["47 49 46 38 39 61", "47 49 46 38 37 61"],
  },
  [TYPES.WEBP]: {
    headerLength: 4,
    hexInfo: "52 49 46 46",
  },
  [TYPES.ICO]: {
    headerLength: 4,
    hexInfo: "00 00 01 00",
  },
  [TYPES.BMP]: {
    headerLength: 2,
    hexInfo: "42 4D",
  },
};
如何查看文件的Hex信息

vscode插件:Hex Editor

image.png

判断传入的文件内容是否与定义好的数据匹配
/**
 *
 * @param {*} file 文件对象
 * @param {*} imageTypes 所需要支持的图片格式
 * @returns {Promise} 返回一个Promise对象
 */
function verifyFormatByBinary(
  file,
  imageTypes = files.DEFAULTIMAGETYPES
) {
  return new Promise(async (resolve, reject) => {
    imageTypes = imageTypes.map((item) => item.toUpperCase());
    const fileHeaderInfo = filterKeys(files.FILE_HEADER_INFO, imageTypes);
    // 遍历文件头匹配信息
    for (const format in fileHeaderInfo) {
      // headerLength:代表格式的文件头长度;hexInfo:代表格式的标准文件头字符串
      const { headerLength, hexInfo } = fileHeaderInfo[format];
      // 将文件对象进行截取
      const sliceFile = file.slice(0, headerLength);
      // 将截取后文件对象转为16进制字符串
      const str = await blobToString(sliceFile);
      // 将字符串与标准字符串进行匹配
      if (Array.isArray(hexInfo) ? hexInfo.includes(str) : str === hexInfo) {
        resolve(str);
      }
    }
    reject(false);
  });
}
依赖:将文件头二进制数据转为十六进制字符串
async function blobToString(blob) {
  // 返回一个二进制ArrayBuffer, ArrayBuffer 对象用来表示通用的原始二进制数据缓冲区。
  const buffer = await blob.arrayBuffer();
  // 创建一个Uint8Array并将其转为数组, Uint8Array 数组类型表示一个 8 位无符号整型数组,创建时内容被初始化为 0。创建完后,可以以对象的方式或使用数组下标索引的方式引用数组中的元素。
  const uintBuffer = [...new Uint8Array(buffer)];
  // 将Uint8Array每个元素转为十六进制,再转大写, 个位数补0
  const hexArray = uintBuffer.map(item =>
    item
      .toString(16)
      .toUpperCase()
      .padStart(2, '0')
  );
  return hexArray.join(' ');
}
依赖:过滤对象的指定属性
/**
 * 过滤对象的指定属性
 * @param {*} obj 对象
 * @param {*} keysToFilter
 * @returns {Object} 返回一个新对象
 */
function filterKeys(obj, keysToFilter) {
  return Object.keys(obj)
    .filter((key) => keysToFilter.includes(key))
    .reduce((newObj, key) => {
      newObj[key] = obj[key];
      return newObj;
    }, {});
}

发布npm包

创建npm账号

www.npmjs.com/

创建npm包

npm init

package name: (validate-image) 
version: (1.0.0) 
description: validate real image
entry point: (index.js) 
test command: 
git repository: 
keywords: image validate 
author: kris_zhang
license: (ISC) 
About to write to /Users/zhangjiewen/Desktop/projects/study/npm/validate-image/package.json:

{
  "name": "validate-image",
  "version": "1.0.0",
  "description": "validate real image",
  "main": "index.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1"
  },
  "keywords": [
    "image",
    "validate"
  ],
  "author": "kris_zhang",
  "license": "ISC"
}

license选择

image.png

其他配置项

docs.npmjs.com/cli/v8/conf…

发布npm包

zhangjiewen@zhangjiewendeMacBook-Pro validate-image % npm config set registry https://registry.npmjs.org/   
zhangjiewen@zhangjiewendeMacBook-Pro validate-image % npm login                                          
npm WARN adduser `adduser` will be split into `login` and `register` in a future version. `adduser` will become an alias of `register`. `login` (currently an alias) will become its own command.
npm notice Log in on https://registry.npmjs.org/
Username: zhangjiewen
Password: 
Email: (this IS public) 474350393@qq.com
npm notice Please check your email for a one-time password (OTP)
Enter one-time password: 91832573
Logged in as zhangjiewen on https://registry.npmjs.org/.

zhangjiewen@zhangjiewendeMacBook-Pro validate-image % npm publish
npm notice 
npm notice 📦  validate-image@1.0.0
npm notice === Tarball Contents === 
npm notice 662B index.js         
npm notice 288B package.json     
npm notice 191B util/hexInfo.json
npm notice === Tarball Details === 
npm notice name:          validate-image                          
npm notice version:       1.0.0                                   
npm notice filename:      validate-image-1.0.0.tgz                
npm notice package size:  765 B                                   
npm notice unpacked size: 1.1 kB                                  
npm notice shasum:        edf0e0b5ddd3e9e7e0eeabf319dcab33cdf96578
npm notice integrity:     sha512-Pyx6NjgFXhu4x[...]FRhqBma86GWyA==
npm notice total files:   3                                       
npm notice 
npm notice Publishing to https://registry.npmjs.org/
+ validate-image@1.0.0

验证npm包

<template>
  <div>
    <el-upload
      class="avatar-uploader"
      :show-file-list="false"
      :before-upload="beforeAvatarUpload"
    >
      <img v-if="imageUrl" :src="imageUrl" class="avatar" />
      <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
    </el-upload>
    <span v-show="validMessage">{{ validMessage }}</span>
  </div>
</template>

<script lang="ts" setup>
import { ref } from "vue";
import { ElMessage } from "element-plus";
import { Plus } from "@element-plus/icons-vue";
import fileUtil from "../utils/index";
import validateImage from "validate-image";

import type { UploadProps } from "element-plus";

const imageUrl = ref("");

const validMessage = ref("");

const beforeAvatarUpload: UploadProps["beforeUpload"] = (rawFile) => {
  fileUtil
    .verifyFormatByBinary(rawFile, ['png', 'webp'])
    .then((hexNumber) => {
      imageUrl.value = URL.createObjectURL(rawFile);
      validMessage.value = `验证成功,文件头Hex为:${hexNumber}`;
    })
    .catch(() => {
      validMessage.value = `验证失败`;
    });
  return true;
};
</script>

<style scoped>
.avatar-uploader .avatar {
  width: 178px;
  height: 178px;
  display: block;
  object-fit: cover;
}
</style>

<style>
.avatar-uploader .el-upload {
  border: 1px dashed var(--el-border-color);
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  transition: var(--el-transition-duration-fast);
}

.avatar-uploader .el-upload:hover {
  border-color: var(--el-color-primary);
}

.el-icon.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 178px;
  height: 178px;
  text-align: center;
}
</style>

更新npm包

更新npm version

每次更新应保证版本号唯一
error code E403
error 403 403 Forbidden - PUT http://af.hikvision.com.cn/artifactory/api/npm/npm-BBG/validate-image - forbidden
error 403 In most cases, you or one of your dependencies are requesting
error 403 a package version that is forbidden by your security policy, or
error 403 on a server you do not have access to.
手动

手动修改package.json中的version,可以成功,但不建议,需要手动commit

自动
npm version [ | major | minor | patch | premajor | preminor | prepatch | prerelease | from-git]

The newversion argument should be a valid semver string, a valid second argument to semver.inc (one of patchminormajorprepatchpreminorpremajorprerelease), or from-git. In the second case, the existing version will be incremented by 1 in the specified field. from-git will try to read the latest git tag, and use that as the new npm version.

npm version功能
major1. 如果没有预发布号,则直接升级一位大号,其他位置都是0,2. 如果有预发布号:中号和小号都为0,则不升级大号,而是将预发布号删掉。即2.0.0-1变为2.0.0,这就是预发布的作用3. 如果中号和小号任意一个不是0, 那么会升级一位大号,其他位置都为0, 清空预发布号。即2.0.1-1变为3.0.0
minor如果没有预发布号,则直接升级一位中号,大号不动,小号置为0如果有预发布号:如果小号为0, 则不升级中号,将预发布号去掉如果小号不为0, 同理没有预发布号
patch如果没有语法不好:直接升级小号,去掉预发布号如果有预发布号:去掉预发布号,其他不动
premajor直接升级大号, 中号和小号置为0, 增加预发布号为0
preminor直接升级中号, 下号置为0,增加预发布号为0
prepatch直接升级小号,增加预发布号为0
prerelease如果没有预发布号:增加小号,增加预发布号为0如果 有预发布号,则不升级预发布号
语义化版本
Code statusStageRuleExample version
First releaseNew productStart with 1.0.01.0.0
Backward compatible bug fixesPatch releaseIncrement the third digit1.0.1
Backward compatible new featuresMinor releaseIncrement the middle digit and reset last digit to zero1.1.0
Changes that break backward compatibilityMajor releaseIncrement the first digit and reset middle and last digits to zero2.0.0

npm publish