【前端】Vue2全家桶案例《看漫画》之番外篇、express上传漫画(可选)
转载请注明出处:http://www.cnblogs.com/shamoyuu/p/vue_vux_app_extra_1.html
项目github地址:https://github.com/shamoyuu/vue-vux-iconan
这一篇属于后台内容,前端小伙伴可以选择阅读。
接口后面都会公开,不会后台的小伙伴可以直接调用。
首先图片存储。图片我会上传到百度的BOS里,是一个支持外链的存储空间,还提供了很多非常方便的功能,后面用到的时候会详细说。
然后数据库用mysql。上传完图片后会把图片的信息都保存起来,方便我们后面调用。
图片处理,会用到gm,因为有些漫画是左右两张放在一张图片上的,我们需要裁切成2份后再上传。
首先我们建表,总共3个表,分别是漫画表(opus)、章节表(chapter)和图片表(picture)
漫画表(opus)
章节表(chapter)
图片表(picture)
然后我们来写一个router吧,它里面有3个接口,分别是添加漫画,重命名文件夹和批处理temp下文件夹。
这个文件是运行在express里的,可以看我以前介绍的在百度BAE搭建node后台的教程:http://www.cnblogs.com/shamoyuu/p/node_bae.html
var express = require("express"); var request = require('request'); var fs = require('fs'); var router = express.Router(); var _ = require("lodash"); var Opus = require("../models/index").Opus; var Chapter = require("../models/index").Chapter; var Picture = require("../models/index").Picture; var gm = require("gm"); var BosClient = require("bce-sdk-js").BosClient; const config = { endpoint: "https://bj.bcebos.com", //传入Bucket所在区域域名 credentials: { ak: "AccessKey", //您的AccessKey sk: "SecretAccessKey" //您的SecretAccessKey } }; var client = new BosClient(config); /** * 啥都没有 */ router.get("/", function (req, res, next) { res.send({ message: "成功", stateCode: 0 }); }); /** * 添加一个漫画 */ router.get("/add", function (req, res, next) { Opus.create({ name: "七龙珠", summary: "很久很久以前,地球上散落着七颗神奇的龙珠,传说只要聚齐它们,神龙就会出现,并可以为人实现一个愿望。为了寻找龙珠,布尔玛和孙悟空踏上了奇妙的寻珠之旅……", author: "鸟山明", cover: "http://iconan.bj.bcebos.com/2%2Fcover.jpg", type: 0, popularity: 0, score: 100, createtime: new Date(), updatetime: new Date() }).then(function () { console.info("插入数据完成"); }); res.send({ message: "成功", stateCode: 0 }); }); /** * 重命名temp文件夹下除ok外所有文件夹 */ router.get("/rename", function (req, res, next) { fs.readdir("temp", function (err, files) { for (var key in files) { var dirname = files[key]; if (dirname != "ok") { fs.renameSync("temp/" + dirname, "temp/第" + dirname.match(/龍珠完全版Vol_(\d+)/)[1] + "卷"); } } }); res.send({ message: "成功", stateCode: 0 }); }); /** * 开始处理temp下所有文件夹 */ router.get("/start", function (req, res, next) { //需要手动修改为当前漫画id var opusid = 2; var chapterid = 1; var chapterPosition = 0; fs.readdir("temp", function (err, files) { console.info(files); var dirFoo = []; for (var key in files) { var chaptername = files[key]; if (chaptername != "ok") { dirFoo.push(function () { return new Promise(function (resolve, reject) { console.info("开始处理章节", chaptername); Chapter.create({ opusid: opusid, name: chaptername, type: 0, position: chapterPosition++, createtime: new Date(), updatetime: new Date() }).then(function (model) { console.info("插入数据完成", model.dataValues); chapterid = model.dataValues.id; fs.readdir("temp/" + chaptername, function (err, files) { var pathUrl = "temp/ok/"; var num = 0; var fooArr = []; fooArr.push(function () { return new Promise(function (resolve, reject) { fs.readdir("temp/ok", function (err, files) { for (var key in files) { fs.unlinkSync("temp/ok/" + files[key]); } resolve(); }); }) }); for (var key in files) { var fileName = files[key]; console.info(fileName); fooArr.push(function () { return new Promise(function (resolve, reject) { var img = gm("temp/" + chaptername + "/" + fileName); //获取图片尺寸 img.size(function (err, size) { //如果图片是横向长方形,那么就从中间裁切成2张单独的图片 if (size.width / size.height > 1) { img.crop(Math.floor(size.width / 2), size.height, Math.floor(size.width / 2), 0) .write(pathUrl + "p" + fill(num++, 4) + ".jpg", function (err) { err && console.info(err); //第二张图片在第一张裁切完再进行 img.crop(Math.floor(size.width / 2), size.height, 0, 0) .write(pathUrl + "p" + fill(num++, 4) + ".jpg", function (err) { err && console.info(err); resolve(); }); }); } else { img.write(pathUrl + "p" + fill(num++, 4) + ".jpg", function (err) { err && console.info(err); resolve(); }); } }); }) }) } //顺序同步执行fooArr里的方法 reduce(fooArr).then(function () { console.info("裁切完成,开始上传"); //清空方法数组 fooArr.length = 0; var picturePosition = 0; //完成后上传 fs.readdir("temp/ok", function (err, files) { for (var key in files) { var fileName = files[key]; if (/^p\d+.jpg$/.test(fileName)) { console.info(fileName); fooArr.push(function () { return new Promise(function (resolve, reject) { console.info("开始上传"); //获取图片尺寸 gm(pathUrl + fileName).size(function (err, size) { var upFileName = opusid + "/" + chapterid + "/" + Date.now() + ".jpg"; console.info(upFileName); //以文件形式上传 client.putObjectFromFile("iconan", upFileName, pathUrl + fileName) .then(function () { Picture.create({ chapterid: chapterid, url: "http://iconan.bj.bcebos.com/" + upFileName, width: size.width, height: size.height, type: 0, position: picturePosition++, createtime: new Date(), updatetime: new Date() }).then(function () { console.info("插入数据完成"); resolve(); }); }) .catch(function () { console.info("上传失败", arguments); reject(); }); }); }) }); } } reduce(fooArr).then(function () { console.info("上传完成"); resolve(); }); }); }) }); }); }) }); } } reduce(dirFoo).then(function () { console.info("最终完成"); }) }); res.send({ message: "正在操作中,请在控制台查看进度", stateCode: 0 }); }); /** * 将数字补位 * @param num 需要补位的数字 * @param n 需要多少位 * @returns {string} */ function fill(num, n) { return (Array(n).join(0) + num).slice(-n); } /** * 让一个promise的数组顺序执行 */ function reduce(arr) { var sequence = Promise.resolve(); arr.forEach(function (item) { sequence = sequence.then(item) }); return sequence } module.exports = router;
然后把下载好的漫画,以章节为目录复制到temp文件夹下,例如下面的《七龙珠》
先调用rename接口,然后调用start接口,就会看到后台在不断处理和上传图片,坐等几小时后,就会按章节分不同文件夹上传的服务器,然后把外链地址保存到数据库的picture表中。
昨晚上传了5小时,就上传完了《柯南》和《七龙珠》,总共10G多的图片,速度还是很不错的。