RN通过Quill实现图片和视频富文本

345 阅读3分钟

基于react-native-cn-quill开发,实现先在本地编辑图片和视频富文本,在某一个时刻将富文本数据上传到云端

那需要解决以下几个问题:

  1. webview需要能够显示本地文件
  2. 在图片或视频的html标签中能够添加唯一标识文件的id属性
  3. 在上传富文本数据到云端时,能检查到文本在编辑时删除了,如果是一个服务器文件,那么要删除服务器文件,避免服务器过多冗余文件,如果是本地文件,可以直接删除本地文件
  4. 第一次从服务器取获取富文本数据去显示图片或视频,如果本地没有文件,需要先显示占位符,下载完文件后再显示真实的文件
  5. 增量更新富文本数据,实现查看历史版本和多人协作编辑(非实时),以及避免全量更新所产生的流量

1. webview需要能够显示本地文件

首先排除base64直接加载文件的方式,因为富文本中的图片和视频可能都不小,所以还是还是用本地文件路径显示比较合理;但是iOS的WKWebView加强了安全性,不再允许跨域访问,所有跨域地址都失效了,包括不再同一文件夹下的CSSJS等文件引用,所以直接加载本地文件路径肯定显示不了。

解决办法: stackoverflow.com/questions/3…

我选择了修改baseURL的方式,因为这比较符合react-native-cn-quill的情况,

只需要修改QuillEditor.js文件中的source={{ html: content }}source={{...props.source, html: content }}再在创建组件时传入baseUrl

<QuillEditor
  webview={{
    allowFileAccess: true,
    scrollEnabled: true,
    nestedScrollEnabled: true,
    source: {
      baseUrl: RNFS.TemporaryDirectoryPath,
    },
  }} />
          

这样只会只需要把图片缓存在tmp文件中,WKWebView就可以正常访问了

2. 在图片或视频的html标签中能够添加唯一标识文件的id属性

这个只需找到显示和的format类,重写它创建node和返回node的实现,再注入重写的js代码 如下,修改图片的format类

<QuillEditor
 customJS={
  const Image = Quill.import('formats/image');
  class ImageBlot extends Image {
    static create(value) {
      var node = super.create();
      Object.getOwnPropertyNames(value).forEach((attribute_name) => {
        node.setAttribute(attribute_name, value[attribute_name]);
      });
      return node;
    }

    static value(domNode) {
      var blot = {};
      domNode.getAttributeNames().forEach((attribute_name) => {
        blot[attribute_name] = domNode.getAttribute(attribute_name);
      });
      return blot;
    }
  }
  Quill.register(ImageBlot);
}
/>    

3.在上传富文本数据到云端时,能检查到文本在编辑时删除了

通过新delta和delta的diff,可以得到本次需要提交的变化,检查变化里面的delete是否为文件

eg: 比如diff结果为(从第5个索引开始,删除了3个长度的内容):

{"ops":[{"retain":5},{"delete":3}]}    

invert后,得到结果为

 {"ops":[{"retain":5},{"insert":{"image":{"id":"xxxx"}}},{"insert":"Wo"}]}

再更加id删除图片

5. 增量更新富文本数据

quill提供了一个表达富文本的数据结构的库quill-delta,该库最终通过json输出数据,而且为处理数据提供了很多方法,可以直接通过操作数据来处理富文本,比如合并2个富文本(contact方法),合并diff(compose方法)数据到富文本等等

版本改变内容改变人时间操作
v1{"ops":[{"attributes":{"bold":true},"insert":"Hello"},{"insert":{"image":{"id":"xxxxx"}]}tommy2023-1-12 9:00第一次创建内容
v2{"ops":[{"retain":16},{"insert":"\n\n"},{"attributes":{"underline":true,"italic":true,"bold":true},"insert":"Earth’s Day"},{"insert":"\n"}]}tommy2023-1-12 9:05添加Earth's Day的文本,并加对其粗斜体下划线
v3{"ops":[{"retain":5},{"insert":"\n"},{"delete":1}]}tommy2023-1-12 10:00删除里面图片

Graphql设计:

var RichTextType = new GraphQLObjectType({
  name: "RichText",
  fields: () => ({
    id: {
      type: new GraphQLNonNull(GraphQLString)
    },
    version: {
      type: new GraphQLNonNull(GraphQLInt)
    },
    editor: {
      type: GraphQLString
    },
    content: {
      type: GraphQLString
    },
    des: {
      type: GraphQLString,
    },
    props: {
      type: GraphQLString,
    },
    delete_at: {
      type: GraphQLLong,
    },
    create_at: {
      type: GraphQLLong,
    },
    update_at: {
      type: GraphQLLong,
    },
  })
})    

客户端第一次需要把所有数据都拉下来,再解析里面的文件id,下载文件,动态更新富文本;之后客户端根据富文本版本增量更新变化数据