使用electron-vue+go写一个处理excel表格小软件(2)

2 篇文章 0 订阅
2 篇文章 0 订阅

问题

使用node-xlsx处理excel一次最多能处理30M的文件,所以来个80M的话就要手动拆成3个文件,这看起来太蠢了,需要换一个能处理大excel文件的库。将界面优化成这样子:
在这里插入图片描述

思路

经过测试,js最受欢迎的库js-xlsx也不能处理80M的excel文件,所以可能需要使用另外一种语言的库,如果使用另外一种语言,需要考虑怎么选语言,上手难度,开发成本,还有就是是否能嵌入到electron的桌面应用里,如果能嵌入,该怎么进行数据通信等等问题。

  1. 在electron里支持用node开辟一个子进程去运行其他语言打包出来的可执行文件,这样子就可以实现electron里跑其他语言写的程序
  2. 经过了解,python和go都比较容易上手,而且python有xlsxWriter库,go有excelize库都可以处理超大的excel文件,都是解析出来一个数组,后面处理数组就行。 因为python是解释型语言,打包成可执行文件时会连解释器一起打包,所以就算只写了几行代码,打包出来的包也会是几百兆,go是编译型语言,编译打包出来的包比较小,嵌入electron以后比较轻量,所以选用了go语言
  3. 因为还不熟悉新语言语法,所以做一个tcp的socket通信,将go进程解析出来的数组传到node进程,处理数组生成文件部分就可以用之前写好的node部分逻辑进行处理了
  4. 之前node进程里只处理了生成文件,如果需要用go的excelize库生成excel格式的话还需要将放置生成文件的文件夹路径返回给go进程,go再批量处理excel文件格式

go部分

主要流程

  1. 创建一个tcp的socket
func createServer() {
	// 建立socket,监听端口  第一步:绑定端口
	netListen, err := net.Listen("tcp", "127.0.0.1:9800")
	CheckError(err)
	// defer延迟关闭改资源,以免引起内存泄漏
	defer netListen.Close()

	Log("Waiting for clients")
	for {
		conn, err := netListen.Accept() // 第二步:获取连接
		if err != nil {
			continue // 出错退出当前一次循环
		}

		Log(conn.RemoteAddr().String(), " tcp connect success")
		// 这句代码的前面加上一个 go,就可以让服务器并发处理不同的Client发来的请求
		go handleConnection(conn) // 使用goroutine来处理用户的请求
	}
}
func handleConnection(conn net.Conn) {
	buffer := make([]byte, 2048)
	for { // 无限循环
		n, err := conn.Read(buffer) // 第三步:读取从该端口传来的内容
		words := "ok"               // 向链接中写数据
		conn.Write([]byte(words))
		if err != nil {
			Log(conn.RemoteAddr().String(), " connection error: ", err)
			return // 出错后返回
		}
		tcpData := string(buffer[:n]) // 接收到的数据
	}
}
  1. 接收待处理excel文件的绝对路径,返回解析出来的数组给node进程处理并生成拆分好的excel文件
  2. 接收放置输出文件的文件夹绝对路径,批量处理里面的excel文件格式

遇到的坑

  1. go一些库比较新的版本需要在https://pkg.go.dev/搜,在github里直接搜名字可能搜不到想要的版本链接,比如excelize这个库,直接在github搜是找不到v2的链接的
  2. 静态语言写起来比动态语言麻烦挺多,走到哪一步都是需要确定类型的
  3. 在和node进程通信的时候要适当地加time.Sleep(),不然调用conn.Write()的时候会使本来应该两次传递的消息变成了一次

node部分

主要流程

  1. 在electron的主进程里开辟一个子进程来运行go程序
const isWin = /^win/.test(process.platform)
console.log(process.platform)
const path = require('path')

let pyProc = null

const createPyProc = () => {
  let port = '4242'
  let script = path.resolve(__dirname, 'go', isWin ? 'testGo.exe' : 'testGo')
  if (process.env.NODE_ENV === 'production') {
    script = path.join(process.resourcesPath, 'app.asar.unpacked/go', isWin ? 'testGo.exe' : 'testGo')
  }
  console.log(script)
  pyProc = require('child_process').execFile(script, [port]) // 开辟一个子进程运行go打包出来的可执行程序
  if (pyProc != null) {
    console.log('child process success')
  }
}

const exitPyProc = () => {
  pyProc.kill()
  pyProc = null
}

app.on('ready', createPyProc)
app.on('will-quit', exitPyProc)
  1. 在界面点击开始处理后,开始与go进程进行tcp通信
async () => { 
	let tcpData = ''
	const client = net.connect({ port: 9800 })
	client.write(JSON.stringify(this.sourcePathList))
	client.on('data', async data => {
	  tcpData += data.toString()
	  if (tcpData.startsWith('[')) {
	    // 第一个通信会回抛数组
	    if (tcpData.endsWith('数据传输结束标记')) {
	      data = JSON.parse(tcpData.replace(/数据传输结束标记$/, ''))
	      for (let i = 0; i < data.length; i++) { // 遍历循环用node处理每个表的数据,拆分并生成文件
	        await splitXlsx(
	          data[i],
	          i + 1,
	          data.length,
	          this.folderPath,
	          this.log,
	          this.sourcePathList.length
	        ).catch(e => {
	          this.log.error = e
	          this.isLoading = false
	        })
	      }
	      tcpData = ''
	      // 处理完以后,传文件夹过去给go批量生成xlsx格式
	      client.write(this.folderPath)
	    }
	  } else {
	    // 之后的通信都是回抛处理状态
	    if (tcpData.startsWith('处理中断')) {
	      this.log.error = tcpData
	      this.isLoading = false
	    } else {
	      this.log.text = tcpData
	      if (this.log.text.startsWith('处理结束')) {
	        this.isLoading = false
	      }
	    }
	    tcpData = ''
	  }
	})
}
  1. 待处理文件绝对路径传给go进程,go进程解析出一个大数组后传回给node进程处理拆分数组并循环生成新的excel文件,最终将输出文件所在文件夹绝对路径传给go进程,go进程批量处理excel文件的样式格式和打印格式

遇到的坑

  1. 因为需要运行在win和mac系统,所以需要根据系统环境来选择go的可执行程序文件
  2. 在打包electron安装包的时候,需要将go的可执行程序文件打包进去,我用的是electron-builder打包,需要在package.json里增加打包配置项
    "build": {
        // ...
        "extraResources": [
          {
            "from": "src/main/go", // go文件夹里有go的可执行程序文件
            "to": "app.asar.unpacked/go"
          }
        ],
        // ...
     }
    
    这样子就相当于直接复制这些不需打包的静态文件去到安装包里的app.asar.unpacked文件夹里面,在生产环境取文件路径时可以这样取
     if (process.env.NODE_ENV === 'production') {
        script = path.join(process.resourcesPath, 'app.asar.unpacked/go', isWin ? 'testGo.exe' : 'testGo')
      }
    
    win/mac系统都是可以这样配置的
  3. 由于go是解析完全部80M的文件,解析出来的数组超级大,在tcp中传数据缓冲区会不够用,所以数据变成分块传输,这样node进程就无法确定数据传输完毕的时机,需要在go进程传输的数据上加上结束标识,这样在node进程就需要自己拼接分块传输的数据,并且识别结束标识。
  4. 在excel中的时间戳和js计算出来的时间戳是不一样的
    两者的换算公式如下:
    js时间戳转excel时间戳
    const  excelTimeNum = (Number(new Date()) / 1000 + 8 * 3600) / 86400 + 70 * 365 + 19
    
    excel时间戳转js时间戳
    const jsTimeNum = new Date(((excelTimeNum - 19 - 70 * 365) * 86400 - 8 * 3600) * 1000)
    

源码链接

electron部分
go部分

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值