本文档主要实现两个目标:
- 用js写一个将上传的图片压缩的方法
- 将自己写的代码同步到github且发布到npm
现在从0零开始操作,一步一步实现这两个目标;
1. Prepare: 先注册NPM和github账号
过程省略,进入各自的官网,创建账号
github: https://github.com/
2. Coding: 结合两个目标,编码
默认你已经安装了node环境,node环境也包括了npm环境
新建工程文件夹,然后执行下面的命令,初始化一个package.json文件
package.json:
package.json就是管理你本地安装的npm包,用于定义了这个项目所需要的各种模块,以及项目的配置信息(比如名称、版本、许可证等元数据)。一个package.json文件可以做如下事情:
展示项目所依赖的npm包
允许你指定一个包的版本[范围]
让你建立起稳定,意味着你可以更好的与其他开发者共享
注意:npm init -y 执行之后,node会自动在当前目录生成一个基本的package.json文件。
npm init -y // 初始化一个package.json文件
默认生产的package.json文件:
{
"name": "image-compress-tool",
"version": "1.0.0",
"description": "A package for compressing a large image to a small size image",
"main": "index.js",
"repository": "https://github.com/yorcent/image-compress-tool.git",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": ["js", "compress", "image"],
"author": "Yorcent Luo",
"license": "ISC"
}
"main"这一项 是默认的js入口文件, 可以修改。
"repository"这一项暂时不填,一会你创建了对应的github仓库再填进去对应的仓库地址
新建index.js文件,编写代码,如:
/*
* @LastEditTime: 2021-05-19 16:54:13
* @LastEditors: Please set LastEditors
* @Description: 图片处理相关方法
* @FilePath: \light_monitor_web\src\utils\imageCompress.js
*/
/**
* @description: 图片压缩函数
* @param {*} img: Image 格式的image源文件
* @param {*} orientation:需要旋转的参数,一般不需要,除非遇到图片旋转问题才需要
* @param {*} compressType:压缩图片的类型:按照size压缩或者按照分辨率(宽度)压缩, 如果0: 按size压缩,如果是按分辨率压缩,则传需要压缩的目标宽度,如500
* @param {*} quality:压缩图片质量:数值越小,代表压缩后的图片质量越低,大小越小
* @param {*} imageFomate:图片格式,如:image/png
* @param {*} returnType: 压缩后返回图片的类型,可选项为:base64 /blob
* @param {*} cb: 回调
* @return {*}
*/
function compress(img, orientation, compressType, quality, imageFomate, returnType, cb) {
let canvas = document.createElement("canvas")
let ctx = canvas.getContext('2d')
//瓦片canvas
let tCanvas = document.createElement("canvas")
let tctx = tCanvas.getContext("2d")
let width = compressType ? compressType : img.width
let height = compressType ? compressType * (img.height / img.width).toFixed(3) : img.height
// 如果图片大于四百万像素,计算压缩比并将大小压至400万以下
let ratio
if ((ratio = width * height / 4000000) > 1) {
console.log("大于400W像素")
ratio = Math.sqrt(ratio)
width /= ratio
height /= ratio
} else {
ratio = 1
}
canvas.width = width
canvas.height = height
// 铺底色
ctx.fillStyle = "#fff"
ctx.fillRect(0, 0, width, height)
// 如果图片像素大于100万则使用瓦片绘制
let count
if ((count = width * height / 1000000) > 1) {
console.log("超过100W像素")
count = ~~(Math.sqrt(count) + 1) // 计算要分成多少块瓦片
// 计算每块瓦片的宽和高
let nw = ~~(width / count)
let nh = ~~(height / count)
tCanvas.width = nw
tCanvas.height = nh
for (let i = 0; i < count; i++) {
for (let j = 0; j < count; j++) {
tctx.drawImage(img, i * nw * ratio, j * nh * ratio, nw * ratio, nh * ratio, 0, 0, nw, nh)
ctx.drawImage(tCanvas, i * nw, j * nh, nw, nh)
}
}
} else {
ctx.drawImage(img, 0, 0, width, height)
}
//修复有的时候上传图片的时候 被旋转的问题
if(orientation !== '' && orientation !== 1){
switch(orientation){
case 6:// 需要顺时针(向左)90度旋转
this.rotateImg(img,'left',canvas)
break
case 8: // 需要逆时针(向右)90度旋转
this.rotateImg(img,'right',canvas)
break
case 3:// 需要180度旋转
this.rotateImg(img,'right',canvas) //转两次
this.rotateImg(img,'right',canvas)
break
}
}
// 进行压缩
if (returnType === 'base64Url') {
let ndata = canvas.toDataURL(imageFomate, quality) // 第一个参数是图片格式,如:image/png; 第二个参数代表压缩后的图片质量,数值越小,代表压缩后的图片质量越低,大小越小
console.log('压缩前:' + img.src.length)
console.log('压缩后:' + ndata.length)
console.log('压缩率:' + ~~(100 * (img.src.length - ndata.length) / img.src.length) + "%")
tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0
cb(ndata, ndata.length)
} else if (returnType === 'blob') {
canvas.toBlob((blob) => {
console.log('压缩前:' + img.raw.size) // 这个时候的压缩率不太准确,因为initSize算得是原图的base64的长度
console.log('压缩后:' + blob.size)
console.log('压缩率:' + ~~(100 * (img.raw.size - blob.size) / img.raw.size) + "%")
tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0
cb(blob, blob.size)
}, imageFomate, quality)
}
}
为了测试代码,我直接写了一个简单的页面,作为图片压缩的工具使用,同时验证代码;
新建test.html文件,编码如下:
<!--
* @Author: yorcent
* @Date: 2021-05-08 14:30:16
* @LastEditTime: 2021-05-19 17:38:59
* @LastEditors: Please set LastEditors
* @Description: In User Settings Edit
* @FilePath: \imageCompressTool\test.html
-->
<!DOCTYPE html>
<html lang="zh-cmn-Hans">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Image Compress Tool</title>
<style>
.originImage {
width: 300px;
height: 300px;
}
.compressImage {
width: auto;
display: block;
}
.fileTip {
color: red;
}
.infoLabel {
color: red;
height: 40px;
width: auto;
}
</style>
</head>
<body>
<noscript>
<strong>We're sorry but vue-antd-pro doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app">
<input id="inputFile" type="file" name="file" onchange="handleFileChange()">
<div class="fileTip"></div>
<img class="originImage">
<div class="infoLabel"></div>
<img class="compressImage">
</div>
<script type="text/javascript" src="./index.js"></script>
<script>
var fileTip = document.getElementsByClassName('fileTip')
var infoLabel = document.getElementsByClassName('infoLabel')
var originImage = document.getElementsByClassName('originImage')
var compressImage = document.getElementsByClassName('compressImage')
var inputFile = document.getElementById('inputFile')
function handleFileChange () {
const files = inputFile.files
if(files && files[0]) {
const file = files[0]
var formatArr = ['jpg', 'png', 'jpeg']
if (!file.type.split('image/')[1] || !formatArr.includes(file.type.split('image/')[1].toLowerCase())) {
fileTip[0].innerHTML = '上传图片只能是jpg/jpeg/png格式!'
return false
}
blob2Base64(file, function (base64Data) { // blob 转base64
let img = new Image()
img.src = base64Data
img.raw = file
originImage[0].src = base64Data
const timout = setTimeout(() => {
if (file.size > (500 * 1024) || img.width > 600) { // // 图片大小大于500K或宽度大于600px就压缩
fileTip[0].innerHTML = ''
var imageDataType = 'base64Url' // blob 或者 base64Url
compress(img, 1, 600, 1, file.type, imageDataType, function (data, size) {
if (imageDataType === 'base64Url') {
data = base642Blob(data) // base64转blob
}
// let newFile = new File( // 也可以手动new一个File格式的文件,第一个参数为文件blob数据,格式是数组;第二个参数是文件名
// [imgBlob],
// file.name
// )
infoLabel[0].innerHTML = `压缩前:${imageDataType === 'blob' ? file.size : base64Data.length} B; 压缩后:${size} B; 压缩率:${imageDataType === 'blob' ? ~~(100 * (file.size - size)/file.size) : ~~(100 * (base64Data.length - size)/base64Data.length)}%` // 大小统计需和文件转换之后的类型保持一致;blob和base64格式统计出来的数据大小是不一样的
file.raw = data // 后端接口需要将文件blob格式的数据放到file.raw中
file.url = URL.createObjectURL(data)
compressImage[0].src = file.url
clearTimeout(timout)
})
} else {
file.base64 = base64Data
infoLabel[0].innerHTML = '图片未达到压缩条件'
compressImage[0].src = base64Data
file.url = URL.createObjectURL(file.raw)
}
}, 500) // 延时添加用来给压缩过程留一些时间,否则会出现压缩失效的情况
})
}
}
</script>
</body>
</html>
测试文件test.html 用到了另外两个比较常用的方法,一般上传文件和文件转换时都用得上,所以这里也加入到index.js文件中:
// base64字符串转blob
function base642Blob (code) {
let parts = code.split(';base64,')
if (parts.length < 2) {
return null
}
let contentType = parts[0].split(':')[1]
let raw = window.atob(parts[1])
let rawLength = raw.length
let uInt8Array = new Uint8Array(rawLength)
for (let i = 0; i < rawLength; ++i) {
uInt8Array[i] = raw.charCodeAt(i)
}
return new Blob([uInt8Array], { type: contentType })
}
// blob转base64字符串
function blob2Base64 (blob, cb) {
let reader = new FileReader()
reader.readAsDataURL(blob)
reader.onloadend = function(e) {
const base64data = e.target.result
cb(base64data)
}
}
然后在浏览器中打开一下test.html, 上传图片,压缩,结果如下:
node test
检查执行结果,初步测试通过。
注意: 一般如果你的npm包有比较方便的测试代码或者用例,更能吸引开发者使用你发布的npm包。所以,一个优秀的npm包还需要更完善的测试环境。这里暂时不深入展示。
新建README.md文件
# image-compress-tool
A package for compressing a large image to a small size image
# install
npm install -g image-compress-tool
# usage
引入index.js文件:
<script type="text/javascript" src="./index.js"></script>
模拟tesh.html中的样例,上传文件调用方法:
compress(img, 1, 600, 1, file.type, imageDataType, function (data, size) {
}
params description:
/**
* @description: 图片压缩函数
* @param {*} img(Image): Image 格式的image源文件
* @param {*} orientation(Number):需要旋转的参数,一般不需要,除非遇到图片旋转问题才需要
* @param {*} compressType(Number):压缩图片的类型:按照size压缩或者按照分辨率(宽度)压缩, 如果值为0: 按size压缩,如果是按分辨率压缩,则传需要压缩的目标宽度,如500
* @param {*} quality(Number):压缩图片质量:数值越小,代表压缩后的图片质量越低,大小越小
* @param {*} imageFomate(String):图片格式,如:image/png
* @param {*} returnType((String)): 压缩后返回图片的类型,可选项为:base64 /blob
* @param {*} cb(fuction(param1, param2)): 回调方法:返回压缩后的文件数据和文件大小
* @return {*}
**/
README.md文件 是方便开发者快速的了解和学习如何使用你这个npm包,非常重要。
3. 将代码上传至github仓库
这个方法有三种,这里使用其中一种。
1.1 新建git仓库
1.2 将新建好的git仓库地址克隆到本地
1.3 将之前创建的npm包的代码文件夹丢到git仓库克隆后的文件夹内,和.git文件在同一级目录
1.4 执行以下命令
git add .
git commit -m "init commit"
git push -u origin master
4.将github仓库地址复制到npm包的package.json里的“repository”中
"repository": "https://github.com/yorcent/image-compress-tool.git",
5. 执行以下命令将完整和正确的代码发布到npm
npm login
// 输入npm账号名
// 输入npm密码
// 输入npm邮箱
npm publish
6. 验证发布是否成功
一般你登录成功之后,常见的发布失败的原因有两个:
1. 包名已经被占用,失败结果如下,或者一些其他的错误提示,其实基本都是包名重复了,查重的方式很简单,直接去npm搜你的包名就好了,如果收到了,那你就得修改包名,直接publish成功为止
2. 包名或者版本号不符合规则,这个字需要根据规则重新修改包名和版本号就行了(发布包的名字不能跟npm网上已有的包重名,不能有大写字母,空格,和下划线)
npm publish成功 之后 会返回给你发布包的名称和版本:
如果没有返回则发布失败,需要重新发布;或者如果你又修改了代码,重新发布的话你需要将包修改版本号之后重新发布
然后你试着npm install -s packageName 检查是否安装到了node-modules目录。最后你可以开始调用你的写的方法。
7. 踩坑
如果你将你的npm镜像改成了淘宝镜像,那么需要将镜像改回城 npm官方镜像,然后再重新npm login, 否则会发布失败。
npm 设置镜像命令:
npm config set registry http://registry.npmjs.org