vue3+electron开发桌面软件(9)——选中多个文件,右键上传

系列文章目录

系列第一篇: vue3+electron开发桌面软件入门与实战(0)——创建electron应用



前言

从本系列第六篇文章开始,我们一共用三篇文章讲解了系统级右键实现文件上传、手动修改注册表实现级联菜单、electron操作修改注册表实现级联菜单。

网络上大部分文章也止步于此,但是在很多业务场景下,我们都需要右键选中多个文件,实现一键上传功能。如图:

在这里插入图片描述

网上部分文章也提到了多选文件(图片)并上传,但是基本都未进行任何代码操作,而是使用鼠标拖动多个文件放到electron应用的图标,利用electron默认的能力实现多文件上传,针对我们具体的需求,这种方式只能算是一种无法解决问题时的妥协方案。


一、我们如何思考

遇到问题我们应该如何思考呢?

通过查阅网络资料,我们可以大胆设想,可能有以下两种方案:

  1. 我们依赖注册表参数“1%”获取文件路径,发现这个参数只能传递一个文件路径。那么有没有一个类似的参数,或者通过某种参数操作,可以获取多个文件路径?
  2. 通过查阅资料,发现有大佬通过cmd命令行传参时,可以控制黑窗口的显示,当出现第二个窗口时,不显示,并把获得的参数传递给第一个窗口,我们能不能借鉴这种方式,把多个文件路径都汇集到第一个应用窗口?

二、解决问题

1.选择方案

老规矩,如果着急借鉴代码实现效果,可以不用看我bb解决问题的方式。直接跳转到最后。

最初我是倾向于借助注册表参数解决问题的,所以研究了很多注册表相关知识,发现以我目前水平,这条路走不通。

所以最终确定为选择第二种方案,后续文件打开的应用窗口将参数传递给第一个窗口。

2. 发现electron多开窗口监听

借鉴网上大佬cmd多窗口时传参的思路,我们如果想要实现electron多窗口传参,就需要在electron打开新窗口的时候,拿到参数,传递给第一个打开的活跃窗口。

这个方案是很麻烦的,全局参数、进程通信(不是electron的进程概念,而是windows进程)、参数管理……想想都头大,所以我开了个头,就放弃继续深入了。因为这时候,我发现这个场景似曾相识。

没错,就是我最开始写这个项目的时候,为了防止多开窗口,在electron的main.js写过一个监听:

    app.on('second-instance', (event, commandLine, workingDirectory, additionalData) => {
    
    
            // 有人试图运行第二个实例,我们应该关注我们的窗口
            if (win) {
    
    
                if (win.isMinimized()) win.restore()
                win.focus()
            }
        })

这个监听就是在做windows层面的进程管理(如果不理解,可以略过这句话,直接看后面的大白话。)

说白了,这个监听就是当我们打开第二个窗口的时候,electron可以监听到,然后我们在里面做了个判断,如果win窗口存在,那么就不要打开窗口了,而是显示我们存在的窗口。

这不就是electron官方提供的多开窗口监听吗,用官方的,省事不说,起码不会有太多bug吧。

如果我们去掉这段监听代码,就会发现如果多选文件后打开应用,那么我们选择了几个文件,就会打开几个应用,并且每个应用都能打印出对应文件的地址。即应用拿到了各自文件的地址。

所以我们现在要做的,就是怎么把后续打开应用中的文件地址,都传递给第一个应用窗口。

3.查找可使用的官方参数

我们为什么要使用大企业背书的框架?就是因为我们相信他们是在做产品,而不是仅仅做个项目。产品,就要考虑更多场景,所以我觉得官方既然想到了多窗口管理,就不会不考虑多窗口参数间的通讯。

果然,经过查阅得知additionalData传递的就是后续打开窗口的参数。代码如下:

        app.on('second-instance', (event, commandLine, workingDirectory, additionalData) => {
    
    
            // 输出从第二个实例中接收到的数据,传入到fileController中
            const url=argvController.getConfigByArgv('getUrl',additionalData.argv)
            fileController.addFileUrl(url)
            // 有人试图运行第二个实例,我们应该关注我们的窗口
            if (win) {
    
    
                if (win.isMinimized()) win.restore()
                win.focus()
            }
        })

不用关心getConfigByArgv方法和fileController.addFileUrl,这只是我封装的方法,如何去操作拿到的文件地址。

这一部分真正核心的代码就是一个参数:additionalData.argv。这里面放着后面窗口的argv参数。而通过本系列前面单文件上传的文章可以知道,argv中保存着文件的地址。

我们拿到所有文件地址后,就可以随意操作多选的文件了。

4.示例代码

  1. main.js中监听多文件打开的窗口参数
        app.on('second-instance', (event, commandLine, workingDirectory, additionalData) => {
    
    
            // 输出从第二个实例中接收到的数据,传入到fileController中
            const url=argvController.getConfigByArgv('getUrl',additionalData.argv)
            fileController.addFileUrl(url)
            // 有人试图运行第二个实例,我们应该关注我们的窗口
            if (win) {
    
    
                if (win.isMinimized()) win.restore()
                win.focus()
            }
        })
  1. getConfigByArgv是我用来解析argv参数的方法,这里为了拓展性写了个策略模式,如果不好接受,不用有心智负担,只需要按照自己喜欢的方式,从argv中拿到文件地址就可。
const argvTypes={
    
    
    //为获取参数设计策略模式
    getUrl(argv){
    
    
        //获取地址
        for (let i = 0; i < argv.length; i++) {
    
    
            if (argv[i].includes('upload-tome') ) {
    
    
                return argv[i + 1]
                break
            }else if(argv[i].includes('upload-toother')){
    
    
                return argv[i + 1]
                break
            }else if(argv[i].includes('upload-backgorundget')){
    
    
                return argv[i + 1]
                break
            }
        }
    },
    getTomeType(argv){
    
    
        //获取tome后面附带的参数值
        for (let i = 0; i < argv.length; i++) {
    
    
            if (argv[i].includes('upload-tome') ) {
    
    
                return argv[i]
                break
            }
        }
    },
    getCmdType(argv){
    
    
        //获取tome、toother两个参数
        let flag='get'
        // console.log(config)
        for (let i = 0; i < argv.length; i++) {
    
    
            // debugger
            if (argv[i].includes('upload-tome') ) {
    
    
                flag=config.toTypeList.tome
                break
            }else if(argv[i].includes('upload-toother')){
    
    
                flag=config.toTypeList.toother
                // flag='tome'
            }else if(argv[i].includes('upload-get')){
    
    
                flag=config.toTypeList.get
            }else if(argv[i].includes('upload-backgorundget')){
    
    
                flag=config.toTypeList.get
            }
        }
        return flag
    }
}
const getConfigByArgv=(type,argv)=>{
    
    
    return argvTypes[type](argv)
}
  1. addFileUrl方法就比较简单了,就是为了得到一个文件数组:
let fileUrlArr = []
//省略无关代码...
const addFileUrl = (v) => {
    
    
    fileUrlArr.push(v)
}
  1. 组合最终的files:
    const progressArgv = progress.argv
    const cwd = progress.cwd()
    let url = argvController.getConfigByArgv('getUrl',progressArgv)  //获取第一实例的文件地址
    let files = []
    files.push(url, ...fileUrlArr)

大家对上面的代码应该不陌生,是系列前面文章里获取文件地址的一段代码,现在我们得到了第二个窗口后的文件数组fileUrlArr,只需要和第一个窗口的文件地址合并为新的数组files,就可得到最终结果。

结合文章开头的图,点击上传后,在文件列表显示已上传的文件详情:

在这里插入图片描述
在这里插入图片描述


总结

至此,关于文件上传的基本功能都已完成,后续可能还会有一些样式的优化,比如正常打开时,应用在屏幕中央,通过文件右键打开时,应用在右下角有一些特殊样式。

用最简单的方式,完成产品的需求,才是一个技术人该有的追求。千万不要成为企业里那个一整天都在反复修改代码、反复解决问题的人。短期看,项目离不开你,长期看,你只能干到35。

能看到这的应该都是对electron比较感兴趣的同学,我最近在参加csdn新星计划导师活动,我对活动细节不了解也不太关心,不过有兴趣的同学可以免费报名,有集中讨论讲解的环节,应该比私信要效率更高。

活动地址请戳:https://bbs.csdn.net/topics/615149275

猜你喜欢

转载自blog.csdn.net/zjsj_lize/article/details/129948441