1. 在HTML文件中引入需要的文件;
<!-- 引入开源的图标库文件 -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.4.0/css/font-awesome.min.css" rel="stylesheet" type="text/css" />
<!-- 引入编辑器的CSS文件 -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/froala-editor/2.5.1/css/froala_editor.pkgd.min.css" rel="stylesheet" type="text/css" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/froala-editor/2.5.1/css/froala_style.min.css" rel="stylesheet" type="text/css" />
<!-- 依赖于JQuery,必须要引入-->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<!-- 引入编辑器的JS文件 -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/froala-editor/2.5.1//js/froala_editor.pkgd.min.js"></script>
<!-- 引入中文语言包对编辑器进行汉化,语言包从以下网址下载-->
<!-- https://www.froala.com/wysiwyg-editor/languages-->
<script type="text/javascript" src="/public/admin/editor/froala_editor_2.8.5/js/languages/zh_cn.js"></script>
2. 在HTML文件中定义一个文本域,指定编辑器要插入的位置;
<textarea name="content" id="content" cols="100" rows="8" style="margin-top:20px"></textarea>
3. 在HTML文件中初始化编辑器;
<script>
// 初始化编辑器
$(function () {
$('#content').froalaEditor({
// 设置编辑器的高度
height: 300,
// 设置所用的语言
language: 'zh_cn',
// 配置上传图片的路由
imageUploadURL:'/admin/goods/goodsUploadImage',
// 配置根据屏幕不同大小显示不同的工具条
// 如果不需要区分显示,需要全部设置成一样的
toolbarButtons: [
'fullscreen', 'bold', 'italic', 'underline', 'strikeThrough', 'subscript', 'superscript',
'|',
'fontFamily', 'fontSize', 'color', 'inlineStyle', 'paragraphStyle',
'|',
'paragraphFormat', 'align', 'formatOL', 'formatUL', 'outdent', 'indent', 'quote',
'-',
'insertLink', 'insertImage', 'insertVideo', 'embedly', 'insertFile', 'insertTable',
'|',
'emoticons', 'specialCharacters', 'insertHR', 'selectAll', 'clearFormatting',
'|',
'print', 'spellChecker', 'help', 'html',
'|',
'undo', 'redo'
],
toolbarButtonsSM: [
'fullscreen', 'bold', 'italic', 'underline', 'strikeThrough', 'subscript', 'superscript',
'|',
'fontFamily', 'fontSize', 'color', 'inlineStyle', 'paragraphStyle',
'|',
'paragraphFormat', 'align', 'formatOL', 'formatUL', 'outdent', 'indent', 'quote',
'-',
'insertLink', 'insertImage', 'insertVideo', 'embedly', 'insertFile', 'insertTable',
'|',
'emoticons', 'specialCharacters', 'insertHR', 'selectAll', 'clearFormatting',
'|',
'print', 'spellChecker', 'help', 'html',
'|',
'undo', 'redo'
],
toolbarButtonsMD: [
'fullscreen', 'bold', 'italic', 'underline', 'strikeThrough', 'subscript', 'superscript',
'|',
'fontFamily', 'fontSize', 'color', 'inlineStyle', 'paragraphStyle',
'|',
'paragraphFormat', 'align', 'formatOL', 'formatUL', 'outdent', 'indent', 'quote',
'-',
'insertLink', 'insertImage', 'insertVideo', 'embedly', 'insertFile', 'insertTable',
'|',
'emoticons', 'specialCharacters', 'insertHR', 'selectAll', 'clearFormatting',
'|',
'print', 'spellChecker', 'help', 'html',
'|',
'undo', 'redo'
]
})
});
</script>
4. 配置初始化编辑器中上传图片的路由;
// router.js
'use strict';
module.exports = app => {
const { router, controller } = app;
router.post('/admin/goods/goodsUploadImage',controller.admin.goods.goodsUploadImage);
};
5. 为编辑器上传图片的路由编写控制器;
// app/controller/admin/goods.js
'use strict';
const Fs = require('fs');
// cnpm i mz-modules --save
// 此模块解决上传卡死
const Pump = require('mz-modules/pump');
const Controller = require('egg').Controller;
class GoodsController extends Controller {
// 编辑器图片上传
async goodsUploadImage() {
// autoFields: true 表示除了文件的其它字段
let parts = this.ctx.multipart({ autoFields: true });
let files = {}, stream;
// 循环获取数据流
while ((stream = await parts()) != null) {
if (!stream.filename) {
// 多文件上传时,只能break,不能return
break;
}
// 文件表单的name
let fieldname = stream.fieldname;
// 获取上传文件路径与入库保存路径
let dir = await this.service.tools.getUploadFile(stream.filename);
// 上传路径
let target = dir.uploadDir;
// 写入上传文件
let writeStream = Fs.createWriteStream(target);
await Pump(stream, writeStream);
// 将多个上传图片统一拼接为一个对象,便于访问
files = Object.assign(files, {
[fieldname]: dir.saveDir
})
}
// 返回数据格式要按编辑器约定的格式
this.ctx.body={
link:files.file
}
}
}
module.exports = GoodsController;
6. 控制器中调用服务中的getUploadFile方法;
// app/service/tools.js
'use strict';
const Path = require('path');
const Service = require('egg').Service;
// 引入格式化时间模块
// cnpm i silly-datetime --save
const Sd = require('silly-datetime');
// 引入模块智能生成上传文件夹
// cnpm i mz-modules --save
const Mkdirp = require('mz-modules/mkdirp');
// 引入生成缩略图的模块
// cnpm install jimp --save
// https://github.com/oliver-moran/jimp/tree/master/packages/jimp
const Jimp = require('jimp');
class ToolService extends Service {
// 获取当前时间戳
async getTime(){
let now = new Date();
return now.getTime();
}
// 处理要上传的文件名
async getUploadFile(filename){
// 获取当前日期
let now = Sd.format(new Date(),'YYYYMMDD');
// 创建文件目录(将存放地址与日期进行拼接);
let dir = Path.join(this.config.uploadDir,now);
// 按dir去创建文件夹,如果没有则生成,如果有则忽略
await Mkdirp(dir);
// 以时间戳命名文件
let timestamp = await this.getTime();
// 图片的保存路径(文件夹 + 时间戳 + 后辍名)
let uploadDir = Path.join(dir,timestamp + Path.extname(filename));
return{
// 上传的地址
uploadDir:uploadDir,
// 保存在数据库的地址
saveDir:uploadDir.slice(3).replace(/\\/g,'/')
}
}
// 生成缩略图方法
async jimpImg(target){
Jimp.read(target, (err, lenna) => {
if (err) throw err;
// 生成200乘以200大小,品质为90,并重命名文件
lenna.resize(200,200).quality(90).write(target+'_200x200'+Path.extname(target));
})
}
}
module.exports = ToolService;
7. 处理编辑器上传图片时,由于Egg安全验证机制导致无法上传的问题;
// config/config.default.js
'use strict';
module.exports = appInfo => {
const config = exports = {};
// use for cookie sign key, should change to your own and keep security
config.keys = appInfo.name + '_1585450669767_9677';
// 上传文件存放的全局地置
config.uploadDir = 'app/public/admin/upload';
// 上传文件的配置
// https://github.com/eggjs/egg-multipart
config.multipart = {
// 允许上传的图片格式
whitelist: ['.png', '.jpg', '.jpeg'],
// 文件允许大小
fileSize: '50mb',
// 上传字段的数量
fields: '50'
}
// 处理编辑器上传图片的安全验证机制
exports.security = {
csrf: {
// 当URL为/admin/goods/goodsUploadImage时,忽略csrf验证
ignore: ctx => {
if (ctx.request.url == '/admin/goods/goodsUploadImage') {
return true;
}
return false;
},
}
}
return config
};
参考文档:
GitHub - froala/wysiwyg-editor: The next generation Javascript WYSIWYG HTML Editor.