浏览器获取word文件数据并填入表单中(实现word一键导入功能)

80 阅读3分钟

因为环境是浏览器,所以没办法用fs读取文件,使用的是FileReader,这个API的详细数据可以查看MDN文档,这里使用onload钩子,在事件对象中获取我们所需要的文件

// template
<el-upload
  action="#"
  list-type="picture-card"
  :limit="1"
  :auto-upload="false"
  accept=".docx"
  :before-upload="beforeUpload"
   :on-change="handleChange"
   :http-request="customUpload"
   v-model:file-list="fileList"
   :class="{ hideUpload: hideUpload }"
>
</el-upload>

// script
const handleChange = (file, uploadFiles) => {
  const reader = new FileReader()
  reader.readAsArrayBuffer(file.raw)
  
  reader.onload = function (event) {
    console.log(event)
    const fileData = event.target.result
  }
  fileList.value.push(file)
}

这里我们拿到了文件的二进制数据,接下来需要通过两个包来解析word中的数据

  • pizzip
  • docxtemplater

注意,template中,accept的值为.doc,可能会报错

Uncaught Error: InternalError: The filetype for this file could not be identified, is this file corrupted ?

const handleChange = (file, uploadFiles) => {
  console.log("[mLog] file -->", file)
  const reader = new FileReader()
  reader.readAsArrayBuffer(file.raw)
  
  reader.onload = function (event) {
    const fileData = event.target.result
    // 在这里可以对文件数据进行进一步处理
    let zip = new Pizzip(fileData)
    const doc = new Docxtemplater(zip)
    console.log("[mLog] doc -->", doc)
  }

  fileList.value.push(file)
}

这时我们可以看控制台

image.png 搜索似乎没法查找未展开的对象中的字段,只能说一顿好找

可以看到这些对象,type有content/tag,这好像是判断内容是双标签,type就是tag,单标签也是content,但是单标签posiztion属性outsidetag,text元素是insidetag,双标签,标签头是start,标签尾是end

我们需要查找对应表格中的数据,因为数据也是键值对模式,所以只需要去掉所有的标签,即过滤出所有type属性为content,position属性为insidetag的元素,这样剩下的就是我们所需求数据的键值对了,在根据index查找对应位置的值即可

补充: 在根据index查找数据位置时,可以发现有些标点/字符也换单独占一个对象,并且需要合并单元格,导致index对应的位置不固定,具体原理没研究清楚,但想了个办法,如果是表格,就获取<w:tc></w:tc>,根据index和表格的单元格查询

贴上完整代码:

// 组件内
const handleChange = (file, uploadFiles) => {
  try {
    // console.log("[mLog] file -->", file)
    const reader = new FileReader()
    reader.onload = function (event) {
      const fileData = event.target.result
      // 在这里可以对文件数据进行进一步处理
      let zip = new Pizzip(fileData)
      const doc = new Docxtemplater(zip)
      // console.log("[mLog] doc -->", doc)
      const arr = doc.compiled["word/document.xml"].lexed
      const newArr = getWordInfo(arr)
      // 将最终结果存入本地,方便路由跳转到添加表单页拿数据
      sessionStorage.setItem("wordInfo", JSON.stringify(newArr))
    }
    reader.readAsArrayBuffer(file.raw)
    fileList.value.push(file)
  } catch (e) {
    ElMessage.error("文件识别出错")
  }
}

// js文件
export const getWordInfo = (word) => {
  // word = word.replace(/<(\w+?)[^>]*?>/g, '').replace(/<\/.*?>/g, '')
  const obj = {}
  word.forEach((item) => {
    switch (item.value) {
      case '信访件编号': obj.number = getValueByTC(word, item.lIndex)
        break
      case '信访人': obj.personName = getValueByTC(word, item.lIndex)
        break
      case '人数': obj.peopleCount = getValueByTC(word, item.lIndex)
        break
      case '证件号码': obj.personId = getValueByTC(word, item.lIndex)
        break
      case '电话号码': obj.personPhone = getValueByTC(word, item.lIndex)
        break
      case '登记单位': obj.registerDepartName = getValueByTC(word, item.lIndex)
        break
      case '初重信': obj.isRepeat = getValueByTC(word, item.lIndex)
        break
      case '信访人地址': obj.personAddress = getValueByTC(word, item.lIndex)
        break
      case '问题发生地': obj.address = getValueByTC(word, item.lIndex)
        break
      case '信访日期': obj.visitTime = getValueByTC(word, item.lIndex)
        break
      case '标题': obj.title = getValueByTC(word, item.lIndex)
        break
      case '信访内容':
        obj.descriptionCategoryText = getValueByTC(word, item.lIndex, 2)
        obj.description = getValueByTC(word, item.lIndex, 4)
        break
      case '经办人': obj.managePersonName = getValueByTC(word, item.lIndex)
        break
      case '经办日期': obj.manageTime = getValueByTC(word, item.lIndex)
        break
      default:
        break;
    }
  });
  return obj
}
// arr值全部数组
// start指当前键所在数组中的index
// num指值的单元格在当前键所在的单元格后,几格位置
const getValueByTC = (arr, start, num = 1) => {
  let str = ''
  for (let i = start + 1; i < arr.length; i++) {
    let item = arr[i]
    let value = item.value
    if (value === '<w:tc>') {
      num--
    }
    if (num <= 0) {
      if (value === '</w:tc>') {
        break;
      }
      if (item.position === "insidetag") {
        str += value
      }
    }
  }
  console.log('[mLog] str -->', str)
  return str;
}

如果是纯文字,暂时没有需求,也没尝试,以后有在补充