.net core vue+wangEditor (双向绑定) 上传图片和视频功能
最终效果,是这样的,现在开始记录怎么做:
开始 npm 安装 wangEditor
安装好后,
因为要用vue 双向绑定 ,所以 我就把wangwangEditor 做成了一个封装组件,先看一下目录 :
我是把wangEditor写在了my-components这个项目下,新建一个 vue组件,代码如下:
- <template>
- <div id="wangeditor">
- <div ref="editorElem" style="text-align:left"></div>
- </div>
- </template>
- <script>
- import E from 'wangeditor'
- export default {
- name: 'editorElem',
- data() {
- return {
- editor: null,
- editorContent: ''
- }
- },
- props: ['catchData', 'content'], // 接收父组件的方法
- watch: {
- content() {
- this.editor.txt.html(this.content)
- }
- },
- mounted() {
- var imgUrl = "";
- this.editor = new E(this.$refs.editorElem)
- this.editor.customConfig.onchange = (html) => {
- this.editorContent = html
- this.catchData(this.editorContent) // 把这个html通过catchData的方法传入父组件
- }
- this.editor.customConfig.uploadImgServer = '/api/Media/OnPostUpload'
- this.editor.customConfig.uploadVideoServer="/api/Media/OnPostUploadVideo" // 或 /node_modules/wangeditor/release/wangEditor.js 里直接写上传视频接口
- // 下面是最重要的的方法
- this.editor.customConfig.uploadImgHooks = {
- before: function (xhr, editor, files) {
- // 图片上传之前触发
- // xhr 是 XMLHttpRequst 对象,editor 是编辑器对象,files 是选择的图片文件
- // 如果返回的结果是 {prevent: true, msg: 'xxxx'} 则表示用户放弃上传
- // return {
- // prevent: true,
- // msg: '放弃上传'
- // }
- },
- success: function (xhr, editor, result) {
- // 图片上传并返回结果,图片插入成功之后触发
- // xhr 是 XMLHttpRequst 对象,editor 是编辑器对象,result 是服务器端返回的结果
- this.imgUrl = Object.values(result.data).toString()
- },
- fail: function (xhr, editor, result) {
- debugger;
- var res = xhr.data;
- // 图片上传并返回结果,但图片插入错误时触发
- // xhr 是 XMLHttpRequst 对象,editor 是编辑器对象,result 是服务器端返回的结果
- },
- error: function (xhr, editor) {
- debugger;
- // 图片上传出错时触发
- // xhr 是 XMLHttpRequst 对象,editor 是编辑器对象
- },
- timeout: function (xhr, editor) {
- // 图片上传超时时触发
- // xhr 是 XMLHttpRequst 对象,editor 是编辑器对象
- },
- // 如果服务器端返回的不是 {errno:0, data: [...]} 这种格式,可使用该配置
- // (但是,服务器端返回的必须是一个 JSON 格式字符串!!!否则会报错)
- customInsert: function (insertImg, result, editor) {
- // 图片上传并返回结果,自定义插入图片的事件(而不是编辑器自动插入图片!!!)
- // insertImg 是插入图片的函数,editor 是编辑器对象,result 是服务器端返回的结果
- // 举例:假如上传图片成功后,服务器端返回的是 {url:'....'} 这种格式,即可这样插入图片:
- let url = Object.values(result.data) // result.data就是服务器返回的图片名字和链接
- JSON.stringify(url) // 在这里转成JSON格式
- insertImg(url)
- // result 必须是一个 JSON 格式字符串!!!否则报错
- },
- };
- this.editor.customConfig.debug = true;
- this.editor.create() // 创建富文本实例
- if (!this.content) {
- this.editor.txt.html('请编辑内容1')
- }
- }
- }
- </script>
然后,再在主页面上,引用 ,和感觉和后端差不多
<template>
<editorElem :catchData="catchData" :content="channelForm.content"></editorElem> (:content 这里就是双向绑定)
</template>
import editorElem from "../../my-components/Editor.vue";
export default {
name: "editor",
components: {
editorElem
}
}
接着,开始改wangEidotr的视频上传代码,原wangEidotr上传视频用的不怎么好,所以我就去网上找了大神的( https://blog.csdn.net/m0_37885651/article/details/83660206 )代码修改了一下,
先找到wangEidotr.js
- //
- /*
- menu - video
- */
- // 构造函数
- function Video(editor) {
- this.editor = editor;
- this.$elem = $('<div class="w-e-menu"><i class="w-e-icon-play"><i/></div>');
- this.type = 'panel';
- // 当前是否 active 状态
- this._active = false;
- }
- // 原型
- Video.prototype = {
- constructor: Video,
- onClick: function onClick() {
- this._createInsertPanel();
- },
- _createInsertPanel: function _createInsertPanel() {
- var editor = this.editor;
- var uploadVideo = editor.uploadVideo;
- var config = editor.config;
- // id
- var upTriggerId = getRandom('up-trigger');
- var upFileId = getRandom('up-file');
- // tabs 的配置
- var tabsConfig = [{
- title: '上传 video',
- tpl: '<div class="w-e-up-img-container">\n ' +
- '<div id="' + upTriggerId + '" class="w-e-up-btn">\n ' +
- '<i class="w-e-icon-upload2"></i>\n </div>\n ' +
- '<div style="display:none;">\n <input id="' + upFileId + '" type="file" multiple="multiple" accept="audio/mp4, video/mp4"/>\n ' +
- '</div>\n </div>',
- events: [{
- // 触发选择视频
- selector: '#' + upTriggerId,
- type: 'click',
- fn: function fn() {
- var $file = $('#' + upFileId);
- var fileElem = $file[];
- if (fileElem) {
- fileElem.click();
- } else {
- // 返回 true 可关闭 panel
- return true;
- }
- }
- }, {
- // 选择视频完毕
- selector: '#' + upFileId,
- type: 'change',
- fn: function fn() {
- var $file = $('#' + upFileId);
- var fileElem = $file[];
- if (!fileElem) {
- // 返回 true 可关闭 panel
- return true;
- }
- // 获取选中的 file 对象列表
- var fileList = fileElem.files;
- if (fileList.length) {
- uploadVideo.uploadVideo(fileList);
- }
- // 返回 true 可关闭 panel
- return true;
- }
- }]
- }
- ]; // tabs end
- // 判断 tabs 的显示
- var tabsConfigResult = [];
- tabsConfigResult.push(tabsConfig[]);
- // 创建 panel 并显示
- var panel = new Panel(this, {
- width: ,
- tabs: tabsConfigResult
- });
- panel.show();
- // 记录属性
- this.panel = panel;
- },
- // 试图改变 active 状态
- tryChangeActive: function tryChangeActive(e) {
- var editor = this.editor;
- var $elem = this.$elem;
- if (editor._selectedImg) {
- this._active = true;
- $elem.addClass('w-e-active');
- } else {
- this._active = false;
- $elem.removeClass('w-e-active');
- }
- }
- };
- /*
- 所有菜单的汇总
- */
- // 存储菜单的构造函数
- var MenuConstructors = {};
- MenuConstructors.video = Video;
- // 构造函数
- function UploadVideo(editor) {
- this.editor = editor;
- }
- // 原型
- UploadVideo.prototype = {
- constructor: UploadVideo,
- // 根据 debug 弹出不同的信息
- _alert: function _alert(alertInfo, debugInfo) {
- var editor = this.editor;
- var debug = editor.config.debug;
- // var debug = true;
- var customAlert = editor.config.customAlert;
- if (debug) {
- throw new Error('wangEditor: ' + (debugInfo || alertInfo));
- } else {
- if (customAlert && typeof customAlert === 'function') {
- customAlert(alertInfo);
- } else {
- alert(alertInfo);
- }
- }
- },
- //插入视频的方法 需要单独定义
- insertLinkVideo:function(link){
- var _this3 = this;
- if (!link) {
- return;
- }
- var editor = this.editor;
- var config = editor.config;
- // 校验格式
- var linkVideoCheck = config.linkVideoCheck;
- var checkResult = void ;
- if (linkVideoCheck && linkVideoCheck === 'function') {
- checkResult = linkVideoCheck(link);
- if (typeof checkResult === 'string') {
- // 校验失败,提示信息
- alert(checkResult);
- return;
- }
- }
- editor.cmd.do('insertHTML', '<video src="' + link + '" style="width:50%;height: 50%;" controls autobuffer autoplay muted/>');
- // 验证视频 url 是否有效,无效的话给出提示
- var video = document.createElement('video');
- video.onload = function () {
- var callback = config.linkVideoCallback;
- if (callback && typeof callback === 'function') {
- callback(link);
- }
- video = null;
- };
- video.onerror = function () {
- video = null;
- // 无法成功下载图片
- _this2._alert('插入视频错误', 'wangEditor: \u63D2\u5165\u56FE\u7247\u51FA\u9519\uFF0C\u56FE\u7247\u94FE\u63A5\u662F "' + link + '"\uFF0C\u4E0B\u8F7D\u8BE5\u94FE\u63A5\u5931\u8D25');
- return;
- };
- video.onabort = function () {
- video = null;
- };
- video.src = link;
- },
- // 上传视频
- uploadVideo: function uploadVideo(files) {
- var _this3 = this;
- if (!files || !files.length) {
- return;
- }
- // ------------------------------ 获取配置信息 ------------------------------
- var editor = this.editor;
- var config = editor.config;
- var uploadVideoServer = "/video/uploadVideo";//上传地址
- var maxSize = * * ; //100M
- var maxSizeM = maxSize / / ;
- var maxLength = ;
- var uploadFileName = "file";
- var uploadVideoParams = config.uploadVideoParams || {};
- var uploadVideoHeaders = {};
- var hooks =config.uploadImgHooks || {};
- var timeout = * * ; //5 min
- var withCredentials = config.withCredentials;
- if (withCredentials == null) {
- withCredentials = false;
- }
- // ------------------------------ 验证文件信息 ------------------------------
- var resultFiles = [];
- var errInfo = [];
- arrForEach(files, function (file) {
- var name = file.name;
- var size = file.size;
- // chrome 低版本 name === undefined
- if (!name || !size) {
- return;
- }
- if (/\.(mp4)$/i.test(name) === false) {
- // 后缀名不合法,不是视频
- errInfo.push('\u3010' + name + '\u3011\u4e0d\u662f\u89c6\u9891');
- return;
- }
- if (maxSize < size) {
- // 上传视频过大
- errInfo.push('\u3010' + name + '\u3011\u5927\u4E8E ' + maxSizeM + 'M');
- return;
- }
- // 验证通过的加入结果列表
- resultFiles.push(file);
- });
- // 抛出验证信息
- if (errInfo.length) {
- this._alert('视频验证未通过: \n' + errInfo.join('\n'));
- return;
- }
- if (resultFiles.length > maxLength) {
- this._alert('一次最多上传' + maxLength + '个视频');
- return;
- }
- // ------------------------------ 自定义上传 ------------------------------
- // 添加视频数据
- var formdata = new FormData();
- arrForEach(resultFiles, function (file) {
- var name = uploadFileName || file.name;
- formdata.append(name, file);
- });
- // ------------------------------ 上传视频 ------------------------------
- if (uploadVideoServer && typeof uploadVideoServer === 'string') {
- // 添加参数
- var uploadVideoServer = uploadVideoServer.split('#');
- uploadVideoServer = uploadVideoServer[];
- var uploadVideoServerHash = uploadVideoServer[] || '';
- objForEach(uploadVideoParams, function (key, val) {
- val = encodeURIComponent(val);
- // 第一,将参数拼接到 url 中
- if (uploadVideoParamsWithUrl) {
- if (uploadVideoServer.indexOf('?') > ) {
- uploadVideoServer += '&';
- } else {
- uploadVideoServer += '?';
- }
- uploadVideoServer = uploadVideoServer + key + '=' + val;
- }
- // 第二,将参数添加到 formdata 中
- formdata.append(key, val);
- });
- if (uploadVideoServerHash) {
- uploadVideoServer += '#' + uploadVideoServerHash;
- }
- // 定义 xhr
- var xhr = new XMLHttpRequest();
- xhr.open('POST', uploadVideoServer);
- // 设置超时
- xhr.timeout = timeout;
- xhr.ontimeout = function () {
- // hook - timeout
- if (hooks.timeout && typeof hooks.timeout === 'function') {
- hooks.timeout(xhr, editor);
- }
- _this3._alert('上传视频超时');
- };
- // 监控 progress
- if (xhr.upload) {
- xhr.upload.onprogress = function (e) {
- var percent = void ;
- // 进度条
- var progressBar = new Progress(editor);
- if (e.lengthComputable) {
- percent = e.loaded / e.total;
- progressBar.show(percent);
- }
- };
- }
- // 返回数据
- xhr.onreadystatechange = function () {
- var result = void ;
- if (xhr.readyState === ) {
- if (xhr.status < || xhr.status >= ) {
- // hook - error
- if (hooks.error && typeof hooks.error === 'function') {
- hooks.error(xhr, editor);
- }
- // xhr 返回状态错误
- _this3._alert('上传视频发生错误', '\u4E0A\u4F20\u56FE\u7247\u53D1\u751F\u9519\u8BEF\uFF0C\u670D\u52A1\u5668\u8FD4\u56DE\u72B6\u6001\u662F ' + xhr.status);
- return;
- }
- result = xhr.responseText;
- if ((typeof result === 'undefined' ? 'undefined' : _typeof(result)) !== 'object') {
- try {
- console.log(result);
- result = JSON.parse(result);
- } catch (ex) {
- // hook - fail
- if (hooks.fail && typeof hooks.fail === 'function') {
- hooks.fail(xhr, editor, result);
- }
- _this3._alert('上传视频失败', '上传视频返回结果错误,返回结果是: ' + result);
- return;
- }
- }
- if (!hooks.customInsert && result.errno == '') {
- // hook - fail
- if (hooks.fail && typeof hooks.fail === 'function') {
- hooks.fail(xhr, editor, result);
- }
- // 数据错误
- _this3._alert('上传视频失败', '上传视频返回结果错误,返回结果 errno=' + result.errno);
- } else {
- if (hooks.customInsert && typeof hooks.customInsert === 'function') {
- hooks.customInsert(_this3.insertLinkVideo.bind(_this3), result, editor);
- } else {
- // 将视频插入编辑器
- var data = result || [];
- // data.forEach(function (link) {
- // console.log(link);
- //
- // });
- _this3.insertLinkVideo(data.url);
- }
- // hook - success
- if (hooks.success && typeof hooks.success === 'function') {
- hooks.success(xhr, editor, result);
- }
- }
- }
- };
- // hook - before
- if (hooks.before && typeof hooks.before === 'function') {
- var beforeResult = hooks.before(xhr, editor, resultFiles);
- if (beforeResult && (typeof beforeResult === 'undefined' ? 'undefined' : _typeof(beforeResult)) === 'object') {
- if (beforeResult.prevent) {
- // 如果返回的结果是 {prevent: true, msg: 'xxxx'} 则表示用户放弃上传
- this._alert(beforeResult.msg);
- return;
- }
- }
- }
- // 自定义 headers
- objForEach(uploadVideoHeaders, function (key, val) {
- xhr.setRequestHeader(key, val);
- });
- // 跨域传 cookie
- xhr.withCredentials = withCredentials;
- // 发送请求
- xhr.send(formdata);
- // 注意,要 return 。不去操作接下来的 base64 显示方式
- return;
- }
- }
- };
- // 修改原型
- Editor.prototype = {
- constructor: Editor,
- // 添加视频上传
- _initUploadVideo: function _initUploadVideo() {
- this.uploadVideo = new UploadVideo(this);
- },
- // 创建编辑器
- create: function create() {
- // 添加 视频上传
- this._initUploadVideo();
- },
- };
1,源码里找到Video 的构造函数 还有它的prototype,替换成第一部分。(有版本是一样的,可以不替换)
2,找到UploadImg的构造函数,还有它的prototype,这是上传图片的,相应的 你在这后面 加上第二部分UploadVideo的构造和原型,这是专门写的上传视频的。
3,在Editor原型上加个方法,_initUploadVideo , 然后在 创建编辑器 create 里面执行 this._initUploadVideo() ,加上就可以。
下面是后端代码:
- #region 上传图片 OnPostUpload
- [HttpPost]
- public async Task<IActionResult> OnPostUpload([FromServices]IHostingEnvironment environment)
- {
- List<string> fileUrl = new List<string>();
- var files = Request.Form.Files;
- if (string.IsNullOrWhiteSpace(environment.WebRootPath))
- {
- environment.WebRootPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot");
- }
- string webRootPath = environment.WebRootPath;
- string filePath = Path.Combine(webRootPath + "\\upload\\images");
- if (!Directory.Exists(filePath))
- {
- Directory.CreateDirectory(filePath);
- }
- foreach (var formFile in files)
- {
- if (formFile.Length > )
- {
- var ext = Path.GetExtension(formFile.FileName);
- if (!pictureFormatArray.Contains(ext.Split('.')[]))
- {
- return new JsonResult(TmpUrl.ErrorInfo("图片格式不正确!", null));
- }
- var fileName = Guid.NewGuid().ToString() + ext;
- var path = Path.Combine(webRootPath + "\\upload\\images", fileName);
- using (var stream = new FileStream(path, FileMode.CreateNew))
- {
- await formFile.CopyToAsync(stream);
- fileUrl.Add($"/api/Media/ShowNoticeImg?filePath={fileName}");
- }
- }
- }
- return new JsonResult(TmpUrl.SuccessInfo("ok!", fileUrl));
- }
- #endregion
- #region 上传视频 OnPostUploadVideo
- [HttpPost]
- public async Task<IActionResult> OnPostUploadVideo([FromServices]IHostingEnvironment environment)
- {
- List<string> fileUrl = new List<string>();
- var files = Request.Form.Files;
- if (string.IsNullOrWhiteSpace(environment.WebRootPath))
- {
- environment.WebRootPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot");
- }
- string webRootPath = environment.WebRootPath;
- string filePath = Path.Combine(webRootPath + "\\upload\\videos");
- if (!Directory.Exists(filePath))
- {
- Directory.CreateDirectory(filePath);
- }
- foreach (var formFile in files)
- {
- if (formFile.Length > )
- {
- var ext = Path.GetExtension(formFile.FileName);
- if (!videoFormatArray.Contains(ext.Split('.')[]))
- {
- return new JsonResult(TmpUrl.ErrorInfo("视频格式不正确!", null));
- }
- var fileName = Guid.NewGuid().ToString() + ext;
- var path = Path.Combine(webRootPath + "\\upload\\videos", fileName);
- using (var stream = new FileStream(path, FileMode.CreateNew))
- {
- await formFile.CopyToAsync(stream);
- //fileUrl.Add($"/upload/videos/{fileName}");
- fileUrl.Add("http://localhost:15429/upload/videos/8e11ae8e-8ecc-4b7c-afac-43601530493f.mp4");
- }
- }
- }
- return new JsonResult(TmpUrl.SuccessInfo("ok!", fileUrl));
- }
- #endregion
- #region 获取图片流 ShowNoticeImg
- public IActionResult ShowNoticeImg(string filePath)
- {
- var contentTypeStr = "image/jpeg";
- string webRootPath = Path.Combine(Directory.GetCurrentDirectory(), $"wwwroot\\upload\\images\\{filePath}");
- using (var sw = new FileStream(webRootPath, FileMode.Open))
- {
- var bytes = new byte[sw.Length];
- sw.Read(bytes, , bytes.Length);
- sw.Close();
- return new FileContentResult(bytes, contentTypeStr);
- }
- }
- #endregion
.net core vue+wangEditor (双向绑定) 上传图片和视频功能的更多相关文章
- Vue.js双向绑定的实现原理
Vue.js最核心的功能有两个,一是响应式的数据绑定系统,二是组件系统.本文仅探究几乎所有Vue的开篇介绍都会提到的hello world双向绑定是怎样实现的.先讲涉及的知识点,再参考源码,用尽可能少 ...
- Vue.js双向绑定的实现原理和模板引擎实现原理(##########################################)
Vue.js双向绑定的实现原理 解析 神奇的 Object.defineProperty 这个方法了不起啊..vue.js和avalon.js 都是通过它实现双向绑定的..而且Object.obser ...
- vue的双向绑定原理及实现
前言 使用vue也好有一段时间了,虽然对其双向绑定原理也有了解个大概,但也没好好探究下其原理实现,所以这次特意花了几晚时间查阅资料和阅读相关源码,自己也实现一个简单版vue的双向绑定版本,先上个成果图 ...
- 西安电话面试:谈谈Vue数据双向绑定原理,看看你的回答能打几分
最近我参加了一次来自西安的电话面试(第二轮,技术面),是大厂还是小作坊我在这里按下不表,先来说说这次电面给我留下印象较深的几道面试题,这次先来谈谈Vue的数据双向绑定原理. 情景再现: 当我手机铃声响 ...
- Vue数据双向绑定原理及简单实现
嘿,Goodgirl and GoodBoy,点进来了就看完点个赞再go. Vue这个框架就不简单介绍了,它最大的特性就是数据的双向绑定以及虚拟dom.核心就是用数据来驱动视图层的改变.先看一段代码. ...
- vue数据双向绑定
Vue的双向绑定是通过数据劫持结合发布-订阅者模式实现的,即通过Object.defineProperty监听各个属性的setter,然后通知订阅者属性发生变化,触发相应的回调. 整个过程分为以下几步 ...
- 【学习笔记】剖析MVVM框架,简单实现Vue数据双向绑定
前言: 学习前端也有半年多了,个人的学习欲望还比较强烈,很喜欢那种新知识在自己的演练下一点点实现的过程.最近一直在学vue框架,像网上大佬说的,入门容易深究难.不管是跟着开发文档学还是视频教程,按步骤 ...
- vue数据双向绑定原理
vue的数据双向绑定的小例子: .html <!DOCTYPE html> <html> <head> <meta charset=utf-> < ...
- Vue.js双向绑定原理
Vue.js最核心的功能有两个,一个是响应式的数据绑定系统,另一个是组件系统.本文仅仅探究双向绑定是怎样实现的.先讲涉及的知识点,再用简化的代码实现一个简单的hello world示例. 一.访问器属 ...
随机推荐
- Qt压缩和解压 zip
zlib编译详见 https://blog.csdn.net/zhangxuechao_/article/details/85049711 下载quazip https://github.com/st ...
- centos7 ntp server & samba
最近公司内部一个需求:必须 Linux建个 ntp server ,并且 Windows可以net time \\ip 访问. 想要解决问题,还得解决前置问题. 服务器不能上网,无法直接访问外部 yu ...
- React中ref的三种用法 可以用来获取表单中的值 这一种类似document.getXXId的方式
import React, { Component } from "react" export default class MyInput extends Component { ...
- unsigned int数据类型最大数
#include <stdio.h> int main() { unsigned , b = ; ) { a++; } printf( ); unsigned ; do { n = n / ...
- 201871010101- 陈来弟《面向对象程序设计(java)》第6-7周学习总结
201871010101- 陈来弟<面向对象程序设计(java)>第6-7周学习总结 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh ...
- 第七周scrum会议
本周会议地点依旧 会议照片: 本周内容: 讨论了进行中的难点 我们正在分析图书馆首页的网页结构以及各种跳转的请求以及链接,为爬虫的实现奠定基础. flask框架我们也遇到了很多问题,正在进行官方文档的 ...
- django之三剑客、静态文件配置、请求响应对象、数据库操作
三剑客 from django.shortcuts import render,HttpResponse,redirect HttpResponse # 返回字符串 render(response, ...
- JavaScript中的this—你不知道的JavaScript上卷读书笔记(三)
this是什么? this 是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件.this 的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式.当一个函数被调用时,会 ...
- python之路—从入门到放弃
python基础部分 函数 初识函数 函数进阶 装饰器函数 迭代器和生成器 内置函数和匿名函数 递归函数 常用模块 常用模块 模块和包 面向对象 初识面向对象 面向对象进阶 网络编程 网络编程 并发编 ...
- Scrapy框架详解
Python网络爬虫Scrapy框架研究 Scrapy1.0教程 Scrapy笔记(1)- 入门篇 Scrapy笔记(2)- 完整示例 Scrapy笔记(3)- Spider详解 Scrapy笔记(4 ...