当前位置: 首页 > 工具软件 > ngx-quill > 使用案例 >

angular7富文本编辑器ngx-quill实现自定义及图片、视频上传功能

傅奕
2023-12-01

quill-editor元素定义

<!-- 默认使用:-->
<quill-editor [(ngModel)]="content"></quill-editor>

<div style="height: 40px;"></div>

<!-- 自定义使用:-->
<quill-editor [(ngModel)]="content">
  <div quill-editor-toolbar>

    <span class="ql-formats">
      <button class="ql-bold" [title]="'Bold'"></button>
      <button type="button" class="ql-italic"></button>
      <button type="button" class="ql-underline"></button>
      <button type="button" class="ql-strike ql-active"></button>
    </span>

    <span class="ql-formats">
      <button type="button" class="ql-blockquote"></button>
      <button type="button" class="ql-code-block"></button>
    </span>

    <span class="ql-formats">
      <button type="button" class="ql-header" value="1"></button>
      <button type="button" class="ql-header" value="2"></button>
    </span>

    <span class="ql-formats">
      <button type="button" class="ql-list" value="ordered"></button>
      <button type="button" class="ql-list" value="bullet"></button>
    </span>

    <span class="ql-formats">
      <button type="button" class="ql-script" value="sub"></button>
      <button type="button" class="ql-script" value="super"></button>
    </span>

    <span class="ql-formats">
      <button type="button" class="ql-indent" value="-1"></button>
      <button type="button" class="ql-indent" value="+1"></button>
    </span>

    <span class="ql-formats">
      <button type="button" class="ql-direction" value="rtl"></button>
    </span>

    <!-- <span class="ql-formats">
        <span class="ql-size ql-picker ql-expanded">
            <span class="ql-picker-label" tabindex="0" role="button" aria-expanded="true" aria-controls="ql-picker-options-0"></span>
            <span class="ql-picker-options" aria-hidden="false" tabindex="-1" id="ql-picker-options-0">
                <span tabindex="0" role="button" class="ql-picker-item" data-value="small"></span>
                <span tabindex="0" role="button" class="ql-picker-item ql-selected"></span>
                <span tabindex="0" role="button" class="ql-picker-item" data-value="large"></span>
                <span tabindex="0" role="button" class="ql-picker-item" data-value="huge"></span>
            </span>
        </span>
        <select class="ql-size" style="display: none;">
            <option value="small"></option>
            <option selected="selected"></option>
            <option value="large"></option>
            <option value="huge"></option>
        </select>
    </span> -->
    
    <span class="ql-formats">
      <button type="button" class="ql-link"></button>
      <button type="button" (click)="imgFun()">
        <svg viewBox="0 0 18 18">
          <rect class="ql-stroke" height="10" width="12" x="3" y="4"></rect>
          <circle class="ql-fill" cx="6" cy="7" r="1"></circle>
          <polyline class="ql-even ql-fill" points="5 12 5 11 7 9 8 10 11 7 13 9 13 12 5 12"></polyline>
        </svg>
      </button>
      <button type="button" (click)="videoFun()">
        <svg viewBox="0 0 18 18">
          <rect class="ql-stroke" height="12" width="12" x="3" y="3"></rect>
          <rect class="ql-fill" height="12" width="1" x="5" y="3"></rect>
          <rect class="ql-fill" height="12" width="1" x="12" y="3"></rect>
          <rect class="ql-fill" height="2" width="8" x="5" y="8"></rect>
          <rect class="ql-fill" height="1" width="3" x="3" y="5"></rect>
          <rect class="ql-fill" height="1" width="3" x="3" y="7"></rect>
          <rect class="ql-fill" height="1" width="3" x="3" y="10"></rect>
          <rect class="ql-fill" height="1" width="3" x="3" y="12"></rect>
          <rect class="ql-fill" height="1" width="3" x="12" y="5"></rect>
          <rect class="ql-fill" height="1" width="3" x="12" y="7"></rect>
          <rect class="ql-fill" height="1" width="3" x="12" y="10"></rect>
          <rect class="ql-fill" height="1" width="3" x="12" y="12"></rect>
        </svg>
      </button>
    </span>

    <span class="ql-formats">
      <select class="ql-align" [title]="'Aligment'">
        <option selected></option>
        <option value="center"></option>
        <option value="right"></option>
        <option value="justify"></option>
      </select>
    </span>
  </div>

  <input type="file" #fileUploadVideo>
  <input type="file" #fileUploadImg>

</quill-editor>

<button (click)="getData()" id="btn">获取数据</button>

rich-text.component.ts

实现思路:将富文本中的图片上传、视频上传上面的按钮实现自定义,即删除class属性,然后自定义点击事件,在富文本中添加对应的input file表单元素。然后通过点击图片、视频图标按钮,调用input选择文件的弹框。

选择文件之后上传到服务器,然后服务器返回对应的链接地址,然后将地址插入到富文本中即可。

图片和视频的上传方式都试通过base64上传:

import { Component, OnInit, ViewChild } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';

@Component({
  selector: 'app-rich-text',
  templateUrl: './rich-text.component.html',
  styleUrls: ['./rich-text.component.css']
})
export class RichTextComponent implements OnInit {

    @ViewChild('fileUploadVideo') fileUploadVideo: any;
    @ViewChild('fileUploadImg') fileUploadImg: any;

    content: any = '';

    public api = 'http://192.168.1.112:8013/ActionApi/File/UploadHttpImage';

    httpOptions = {
        headers: new HttpHeaders().set('Content-Type', 'application/json').set('Content-Type', 'application/x-www-form-urlencoded')
    };

    constructor(public http: HttpClient) { }

    ngOnInit() {}

    getData() {
        console.log(this.content);
    }

    // 上传视频
    videoFun() {
        const that = this;

        // 调用弹框
        this.fileUploadVideo.nativeElement.click();

        this.fileUploadVideo.nativeElement.onchange = function() {
            const filereader = new FileReader();
            const fileType = this.files[0].type; // 获取文件类型

            if (!/video\/\w+/.test(fileType)) {
                alert('看清楚,这个需要视频!');
                return false;
            }
            
            if (this.files[0]) {
                if (this.files[0].size / 1024 / 1024 > 100) {
                    alert('视频上传大小不要超过100MB, 请重新上传!');
                    return;
                }
            }

            const videoName = this.files[0].name;
            filereader.readAsDataURL(this.files[0]); // 将文件以Data URL形式读入页面

            filereader.onload = () => {
                const result = filereader.result;
                if (result) {
                    // ajax数据请求
                    const params = `Base64Video=${result}&Base64VideoName=${videoName}`;
                    that.http.post(that.api, params, that.httpOptions).subscribe((data: any) => {
                        console.log(data);
                        if (data.IsSucceed) {
                            const video = `<iframe src="${data.Data}" width="60%" height="200" align="center"></iframe>`; // 此处必须是iframe,不支持video标签
                            that.content += video;
                        } else {
                            alert('上传失败,请重新上传');
                        }
                    });
                }
            };
        };
    }

    // 图片上传
    imgFun() {
        const that = this;

        // 调用弹框
        this.fileUploadImg.nativeElement.click();

        this.fileUploadImg.nativeElement.onchange = function() {
            const filereader = new FileReader();
            const file = this.files[0]; // 获取文件类型

            if (!/image\/\w+/.test(file.type)) {
                alert('看清楚,这个需要图片!');
                return false;
            }

            if (this.files[0]) {
                if (this.files[0].size / 1024 / 1024 > 2) {
                    alert('图片上传大小不要超过2MB, 请重新上传!');
                    return;
                }
            }

            const imgName = file.name;
            filereader.readAsDataURL(file);

            filereader.onload = () => {
                const result = filereader.result;
                if (result) {
                    // ajax数据请求
                    const params = `Base64Image=${result}&Base64ImageName=${imgName}`;
                    console.log(result);
                    that.http.post(that.api, params, that.httpOptions).subscribe((data: any) => {
                        console.log(data);
                        if (data.IsSucceed) {
                            const img = `<img src=${data.Data} />`; // 此处必须是写死的动态值,不能动态创建img标签
                            that.content += img;
                        } else {
                            alert('上传失败,请重新上传');
                        }
                    });
                }
            };
        };
    }

}

上传存在的问题

前台将图片视频转成base64通过post提交到后台之后,会出现如下的错误:不同的不同不同的错误,比如:参数无效,长度溢出等等。

核心原因:base64的数据传到后台之后会把json数据中的base64数据中的+号变成空格,导致的问题。

解决方法:编码问题

第一种:前端解决(未尝试)

'+' 字符在js传到后台的时候 都会被转义为' ' 的,所以可以将json字符串进行Base64转码之后再传到后台,安全性高一点,需要导入base64.js包代码如下:
    var  jsonStr = JSON.stringify(json)
     var base = new Base64();
     var jsonbase = base.encode(jsonStr)
     //这样就把json数据转码成为了base64字符串,将这个字符串做参数传到后台就行了,就避免了'+'被转义为' ' 

第二种:后端解决

后端在转码的过程中设置和前端统一的编码格式,将所有的空格 ' ' 转成 +即可。

如果出现转码问题,还有就是base64转码的过程中,如果位数不够,后面的需要=补齐。

视频显示问题

在视频上传成功之后,正确的视频地址却无法访问,需要单独在IIS里面设置一下格式即可访问。

 类似资料: