图片切割部分参考:https://www.cnblogs.com/gating/p/12488443.html
其他方法: https://www.jianshu.com/p/8c08a6b23054
https://www.cnblogs.com/zycbloger/p/6230155.html
文件结构部分
imgclip
images ---图片切割后的存放目录
img ---源图片文件夹
node_modules --依赖包
static ---存放静态html文件
index.html
app.js ---静态伺服启动文件
clip_config.js ---切割配置
clip_images.js ---切割图片模块
package.json
实现图片切割过程:
切割图片主要依赖 canvas库
const {
createCanvas,
loadImage
} = require("canvas");
将图片用cavas转为画板 , 然后再讲画板切割出来 , 再然后将切割出来的画板 , 转为图片
压缩图片过程
压缩图片主要依赖 canvas库
var images = require("images");
cavas模块, images模块 , npm下载不顺的话 , 可以尝试yarn 和 cnpm
渲染页面
主要依赖app.js 配置静态伺服
指定端口和静态文件目录 , 监听路由
检测文件, 判断文件, 处理文件
动态渲染dom结构
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <title>Document</title> <style> body { width: 960px; margin: 0px auto; } html, body { margin: 0px; padding: 0; } .cell { float: left; margin: 10px; position: relative; width: 200px; height: 230px; cursor: pointer; } .cell span, .cell img { position: absolute; } .cell img { width: 200px; height: 200px; top: 0; } .cell span { width: 200px; height: 30px; line-height: 30px; top: 200px; left: 0px; text-align: center; font-size: 18px; } img { display: block; } img { display: block; border: 0; } .warp { width: 100%; overflow: hidden; } .warp img { position: relative; top: 0; left: 50%; transform: translateX(-50%); } </style> </head> <body> <div class="warp">
app.js
// 引入内置模块 var http = require('http'); var fs = require('fs'); var url = require('url'); var path = require('path'); var querystring = require('querystring'); const { resolve } = require('path'); //导入配置 const { entry, output, imgPrefix, imgUrl } = require('./clip_config.js'); // 配置路径 const entryPath = __dirname + "/" + entry const outputPath = __dirname + "/" + output // 引入自定义模块 var clip_images = require('./clip_images.js'); // 创建端口 var port = 3000; // 创建服务 var server = http.createServer(); console.log(imgUrl) // 监听服务 server.on("request", function (req, res) { if (req.url !== "/favicon.ico") { var urlObj = url.parse(req.url, true); var pathname = urlObj.pathname; if (pathname == "/" && req.method == "GET") { resHead(".html") res.end("<h1>请进入images目录</h1>"); return; } if (pathname == "/images" && req.method == "GET") { let saveImageName = ''; //原名 let currentImageName = ''; fs.readdir(entryPath, function (err, files) { if (files.length) { files.forEach((file, index) => { saveImageName = entryPath + "/" + file; currentImageName = entryPath + "/aaa" + getExtname(file); fs.rename(saveImageName, currentImageName, (err) => { if (err) throw err; clip_images("aaa" + getExtname(file)) fs.rename(currentImageName, saveImageName, (err) => { if (err) throw err; }) }) }) } else { throw err; } }) if (isDir(outputPath)) { rDir(outputPath); } else { } //配置静态图片 } else if (pathname == "/static/img/file.jpg" && req.method == "GET") { fs.readFile("./static/img/file.jpg", function (err, data) { resHead(".jpg"); res.end(data); }) } else if (pathname == "/static/img/css.jpg" && req.method == "GET") { fs.readFile("./static/img/css.jpg", function (err, data) { resHead(".jpg"); res.end(data); }) } else if (pathname == "/static/img/html.jpg" && req.method == "GET") { fs.readFile("./static/img/html.jpg", function (err, data) { resHead(".jpg"); res.end(data); }) } else if (pathname == "/static/img/js.jpg" && req.method == "GET") { fs.readFile("./static/img/js.jpg", function (err, data) { resHead(".jpg"); res.end(data); }) } else if (pathname == "/static/img/json.jpg" && req.method == "GET") { fs.readFile("./static/img/json.jpg", function (err, data) { resHead(".jpg"); res.end(data); }) } else if (pathname == "/static/img/txt.jpg" && req.method == "GET") { fs.readFile("./static/img/txt.jpg", function (err, data) { resHead(".jpg"); res.end(data); }) } fs.readFile("." + pathname, function (err, data) { fs.stat("." + pathname, function (err, stats) { if (err) { resHead(".html"); res.end("<h1>No file or No dir"); return; } if (stats.isDirectory()) { rDir("." + pathname) } else { var mine = getExtname(pathname); resHead(mine); res.end(data) } }) }); function isDir(p) { //判断是否是文件夹,如果是文件夹返回true,如果不是返回false var stats = fs.statSync(p); var flag = stats.isDirectory(); return flag; } function rFile(p) { //如果不是文件夹,读取对应的文件,并展示 fs.readFile(p, "utf8", function (err, data) { var mine = getExtname(pathname); resHead(mine); res.end(data); }) } function compareFiles(a, b) { console.log(a, b) return 1 } function rDir(p) { //如果是文件夹,读取对应的文件夹,并展示 var str = ""; fs.readdir(p, function (err, files) { if (files.length) { //files获取出来的 var data = fs.readFileSync("./static/index.html", "utf8"); str += data; //排序 var lastNumRes = /d{1,3}.(png|jpg|gif|jpeg|webp)$/g; var filesArr = []; files.forEach((file, i) => { filesArr[i] = file.replace(lastNumRes, (i + 1) + getExtname(file)); }) //渲染 for (var i = 0; i < filesArr.length; i++) { var extname = getExtname(filesArr[i]); //后缀 str += setHtml(extname, filesArr[i], req.url); //渲染函数 } str += ` <script> function fun(a,b){ window.location.href = a + "/" + b; } </script> </body></html>`; resHead(".html"); res.end(str); } else { resHead(".html"); str += "<h1>This folder has no content</h1>"; res.end(str); } }) } function getExtname(p) { //获取文件的后缀名 var ext = path.extname(p); return ext; } function setHtml(extname, p, q) { //渲染HTML模板 var extname = extname.slice(1) if (extname == "html") { return `<div class="cell" onclick=fun('${req.url}','${p}')> <img src="/static/img/${extname}.jpg" alt=""> <span>${p}</span> </div>` } else if (extname == "css") { return `<div class="cell" onclick=fun('${req.url}','${p}')> <img src="/static/img/${extname}.jpg" alt=""> <span>${p}</span> </div>` } else if (extname == "js") { return `<div class="cell" onclick=fun('${req.url}','${p}')> <img src="/static/img/${extname}.jpg" alt=""> <span>${p}</span> </div>` } else if (extname == "json") { return `<div class="cell" onclick=fun('${req.url}','${p}')> <img src="/static/img/${extname}.jpg" alt=""> <span>${p}</span> </div>` } else if (extname == "txt") { return `<div class="cell" onclick=fun('${req.url}','${p}')> <img src="/static/img/${extname}.jpg" alt=""> <span>${p}</span> </div>` } else if (extname == "png") { return `<img src="${imgUrl?imgUrl:q}/${p}" alt="">` } else if (extname == "jpg") { return `<img src="${imgUrl?imgUrl:q}/${p}" alt="">` } else if (extname == ".jpeg") { return `<img src="${imgUrl?imgUrl:q}/${p}" alt="">` } else if (extname == ".png") { return `<img src="${imgUrl?imgUrl:q}/${p}" alt="">` } else if (extname == "") { return `<div class="cell" onclick=fun('${req.url}','${p}')> <img src="/static/img/file.jpg" alt=""> <span>${p}</span> </div>` } else { return `<h1>不支持该文件类型</h1>` } } function resHead(p) { //根据文件类型设置请求头 if (p == "") { return res.writeHead(200, { 'Content-Type': 'image/jpg' }); } else if (p == ".css") { return res.writeHead(200, { 'Content-Type': 'text/css; charset=utf-8' }); } else if (p == ".js") { return res.writeHead(200, { 'Content-Type': 'text/javascript; charset=utf-8' }); } else if (p == ".html") { return res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }); } else if (p == ".json") { return res.writeHead(200, { 'Content-Type': 'text/json; charset=utf-8' }); } else if (p == ".txt") { return res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' }); } else if (p == ".png") { return res.writeHead(200, { 'Content-Type': 'image/png' }); } else if (p == ".jpg") { return res.writeHead(200, { 'Content-Type': 'image/jpg' }); } else if (p == ".jpeg") { return res.writeHead(200, { 'Content-Type': 'image/jpg' }); } else if (p == ".gif") { return res.writeHead(200, { 'Content-Type': 'image/gif' }); } } } }) // 监听端口 server.listen(port); console.log("server running in 127.0.0.1:" + port);
clip_images.js
// 创建写入流 const { createWriteStream } = require("fs"); const fs = require('fs'); const path = require('path') var images = require("images"); // 导入canvas库,用于裁剪图片 const { createCanvas, loadImage } = require("canvas"); //导入配置 let { entry, output, imgPrefix, imgHeight } = require('./clip_config.js'); // 配置路径 const entryPath = __dirname + "/" + entry const outputPath = __dirname + "/" + output module.exports = async function clip_images(imgName) { //删除输出路径下的所有文件 function deleteFolder(path) { let files = []; if (fs.existsSync(path)) { files = fs.readdirSync(path); files.forEach(function (file, index) { let curPath = path + "/" + file; if (fs.statSync(curPath).isDirectory()) { deleteFolder(curPath); } else { fs.unlinkSync(curPath); } }); // fs.rmdirSync(path); } } deleteFolder(outputPath) // 加载图片 const image = await loadImage(entryPath + '/' + imgName); // 获取图片宽高 const { width, height } = image; // 创建等宽登高的canvas const mainCanvas = createCanvas(width, height); // 获取canvas上下文 const ctx = mainCanvas.getContext("2d"); // 绘图 ctx.drawImage(image, 0, 0); // oneHeight 获取一份的高度, canvasX: canvas中的 X , 从哪里开始画 let canvasX, oneHeight; canvasX = oneHeight = imgHeight; // 明确我们需要垂直分割成几份 const num = (height / oneHeight) > Math.floor(height / oneHeight) ? //总张数 Math.floor(height / oneHeight) + 1 : height / oneHeight; const lastHeight = (1 + (height / oneHeight - num)) * oneHeight; //最后一张的高度 let imgUrl = ''; for (let i = 0; i < num; i++) { if (i == (num - 1)) { oneHeight = lastHeight; } // 创建一份裁剪的canvas let clipCanvas = createCanvas(width, oneHeight); // 获取canvas上下文 const clipCtx = clipCanvas.getContext("2d"); // 通过 clipCtx 裁剪 mainCanvas clipCtx.drawImage( mainCanvas, 0, canvasX * i, width, oneHeight, 0, 0, width, oneHeight ); imgUrl = outputPath + `/${imgPrefix+(i + 1)}` + path.extname(imgName); fs.writeFileSync(imgUrl, clipCanvas.toBuffer(), 'binary', function (err) { }) images(imgUrl) .save(imgUrl, { quality: 95 //保存图片到文件,图片质量为50 }); // 主动释放内存 clipCanvas = null; } };
clip_config.js
const ConfigObj = { imgHeight: 350, //裁剪图片的高度 entry: "img", //输入路径 output: "images", //输出路径 imgPrefix: "aaa_", //输出图片名前缀 // imgUrl: "https://img.hbhcdn.com/zhuanti/20841" //配置图片src } module.exports = ConfigObj;
package.json
archiver 压缩文件库
canvas库,用于裁剪图片
compressing 压缩与解压
images 图片处理
unzip 解压zip
bcryptjs 加密
{ "name": "clip", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo "Error: no test specified" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "archiver": "^5.2.0", "bcryptjs": "^2.4.3", "canvas": "^2.6.1", "compressing": "^1.5.1", "images": "^3.2.3", "unzip": "^0.1.11" } }
...