2022 年 2 月 14 日更新
- 掘金的 markdown 解析规则可能改了,两年前的文章样式全崩了,现已修复
前言
近期接到的众多需求里,有一个需求挺有意思,为了完成它,头发又掉了不少...
废话不多说,先来介绍一下需求吧:要求用户在管理后台能编写微信公众号格式的文章,编写过程中要求保持格式,插入的图片这种,在第二行插入的绝不能在第三行显示...
- 图片来源于网络,如有侵权请私信删除
具体可以参考 135 微信文章编辑
分析
讲真,这种需求之前还真没搞过,查了很多富文本编辑器之后,发现都比较难搞,而且很多富文本编辑器,demo 都出问题...
后来发现了 vue-quill-editor
非常不错,功能多且强大,但是 vue-quill-editor
上传图片是将图片转为 base64 编码
,所以就用到了他的 自定义上传
的功能,我们可以利用他的自定义上传功能调取 element
的上传组件,完成我们的需求
接下来就是图片存在哪了,直接扔到 OSS
上,然后 数据库
存 URL
,让前端加载去吧...
正常上传图片流程:
- 用户发送上传 Policy 请求到应用服务器
- 应用服务器返回上传 Policy 和签名给用户
- 用户直接上传数据到 OSS
But !!
由于公司没做内网穿透,设置上传回调就成了问题...
具体参考:help.aliyun.com/document_de…
其实倒也是有办法解决,但当时时间很紧迫,为了抓紧时间上线项目,只能变通一下咯,于是上面提到的流程变成了:
- 客户端上传图片至应用服务器
- 应用服务器直传 OSS
- 返回链接给客户端
没错,变成了服务端上传,这么搞其实有一些 风险
的,但考虑到时间紧,上线后后台又只有一个人管理,上传频率也不是很高,影响不是很大...
最终决定,使用 vue-quill-editor
的自定义上传调用 element upload
,element upload
上传图片至服务端,服务端使用 Go
的 web
框架 Gin
转存至 OSS
上,成功上传后将图片地址返回给 element upload
,element upload
再将地址回传给 vue-quill-editor
开发
确定好方案后,开始施工啦!
-
vue-quill-editor
安装与自定义上传 -
npm install vue-quill-editor --save
import VueQuillEditor from 'vue-quill-editor'
Vue.use(VueQuillEditor);
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
前端逻辑
- html
<quill-editor
v-model="content"
:options="editorOption"
ref="QuillEditor">
</quill-editor>
<el-upload
:data=""
:multiple=""
:show-file-list=""
:on-success=""
class=""
drag
:http-request="uploadImg"
>
</el-upload>
-
el-upload 参数可以根据场景自己填写,关键点在于
http-request
, 选中图片后会直接调用此事件 -
vue-quill-editor
各种配置
const toolbarOptions = [
['bold', 'italic', 'underline', 'strike'], // toggled buttons
['blockquote', 'code-block'],
[{'header': 1}, {'header': 2}], // custom button values
[{'list': 'ordered'}, {'list': 'bullet'}],
[{'script': 'sub'}, {'script': 'super'}], // superscript/subscript
[{'indent': '-1'}, {'indent': '+1'}], // outdent/indent
[{'direction': 'rtl'}], // text direction
[{'size': ['small', false, 'large', 'huge']}], // custom dropdown
[{'header': [1, 2, 3, 4, 5, 6, false]}],
[{'color': []}, {'background': []}], // dropdown with defaults from theme
[{'font': []}],
[{'align': []}],
['link', 'image', 'video'],
['clean'] // remove formatting button
]
export default {
data () {
return {
content: '',
editorOption: {
modules: {
toolbar: {
container: toolbarOptions, // 工具栏
handlers: {
'image': function (value) {
if (value) {
// 这里最重要
// 在编辑器中点击图片 icon 会触发此事件
// 需自己写方法触发 <el-upload>.click()
// 使用 <el-upload> 完成上传,在回调到
} else {
this.quill.format('image', false);
}
}
}
}
}
}
}
}
}
- 由于我们使用 http-request 自定义了上传事件,上传完成后就可以将图片插入到
vue-quill-editor
中
uploadImg(res) {
axios.post('服务端地址', res.file, {
headers: {"content-type": "multipart/form-data"}
}).then(res => {
let quill = this.$refs.QuillEditor.quill
// 如果上传成功, 获取光标所在位置, 插入图片,res 为服务器返回的图片链接地址
if (res) {
let length = quill.getSelection().index;
quill.insertEmbed(length, 'image', res)
// 调整光标到最后
quill.setSelection(length + 1)
} else {
// fail...
}
})
}
后端逻辑
我们使用 Gin 来获取前端上传的图片并转存至 OSS 上
func UploadToOss(c *gin.Context) (url string, err error) {
file, err := c.FormFile("file")
if err != nil {
return "", err
}
fileHandle, err := file.Open() //打开上传文件
if err != nil {
return "", err
}
defer fileHandle.Close()
fileByte, err := ioutil.ReadAll(fileHandle) //获取上传文件字节流
if err != nil {
return "", err
}
url, err = uploadOss(file.Filename, fileByte)
return url, err
}
func uploadOss(fileName string, fileByte []byte) (url string, err error) {
// oss 配置
endpoint := ""
accessKeyId := ""
accessKeySecret := ""
bucketName := ""
domain := ""
// 创建 OSS Client 实例
client, err := oss.New(endpoint, accessKeyId, accessKeySecret)
if err != nil {
return url,err
}
// 获取存储空间
bucket, err := client.Bucket(bucketName)
if err != nil {
return url,err
}
// 随机数防止文件重复
rand.Seed(time.Now().Unix())
randNum := rand.Int()
fileName = cast.ToString(randNum) + fileName
//上传阿里云路径
yunFileTmpPath := filepath.Join("OSS 存储路径", "可选参数") + "/" + fileName
// 上传Byte数组
err = bucket.PutObject(yunFileTmpPath, bytes.NewReader([]byte(fileByte)))
if err != nil {
return url,err
}
return domain + "/" + yunFileTmpPath ,nil
}
总结
第一次写文章,上述如有不清晰,不正确等信息还请多指教。
代码只写了必要逻辑,大家可以结合自己的实际场景处理结果。