必要的上下文

想尽快熟悉上下文语境的,可以点这里: https://github.com/electron/electron/issues/749

这段讨论,其实本来是讨论如何自动设置 input 标签的值来实现自动选择文件的.前一段有个 Electon 中自动上传文件的需求,被 Google 带到了这个讨论地址.虽然,最后当时是采用的不同讨论中的本地代理器转发cookie的策略,但不得不承认,这些讨论还是给了自己很大启发的 -- 虽然暂时并没有什么用.

It's near impossible to programmatically upload a file in electron right now.

当时,讨论区 @erikmellum 的一句 "现在在Electron 中,以编码方式上传文件,几乎是不可能的",让我放弃了对 Electron 本身机制的思考.转而,基于当时 App 已有的本地代理服务器, 做了另一番尝试.当然,最后也是成功了.这个机制,等会儿我会简单描述下.因为它已经不是重点了! 因为已经有了更简化的方式.

这个问题的关键是在于如何获取完整的 cookie,特别是 session 相关那一部分的 cookie.今天突然又看到 electron 文档的 session 部分,看到它有一个 cookies 属性.心想,这个 cookies, 既然是属于 electron 自有的 api,那岂不是也可以获取完整的 cookie ?试了下,还真是! 卒~

技术关键点分析

Electron,结合了 Node 和 Chromium.在相当程度上,可以认为它同时拥有了 Node 和 Chromium 的能力;另一方面,其实也可以认为,它拥有了 Node 和 Chromium 各自的限制.在 Electron 编码时,如何理解和运用 Node 和 Chromium 各自的限制和能力,就变得很有趣.如果能进一步地熟练打通 Node 和 Chromium ,真的可以做某些以前很难去想象的事.

具体到以编码方式上传文件这个问题上.这个问题的完整描述应该是类似于这样: 网站有自己的登录认证机制,在不需要在对网站登录机制做任何修改的前提下,如何自动上传用户相关的文件,比如用户头像?

我们就以自动上传用户头像为例.我们可以假定已经通过某种方式,得到了用户头像的本地路径.--这个大前提,在基于 Electron 的App中,非常容易满足!

对于 Chromium 侧的童鞋来说,拿到文件的本地路径后,是没有比较简便的办法实现文件上传的. https://github.com/electron/electron/issues/749#issuecomment-145764807 讨论中,提到的两个地址,再结合 https://developer.mozilla.org/en-US/docs/Web/API/FormData/Using_FormData_Objects 或许应该也是可以的.我没验证!

对于 Node 层的同学来说,有了文件路径,可以很容易地通过 https://www.npmjs.com/package/request 库来实现文件的上传,如果他能拿到当前回话的完整 Cookie 的话.当然,这个限制,也是有足够多的方式来弥补的,比如让用户在桌面 App 上,再单独登录一次.不管怎样,解决问题就好.

但是,Electron 提供了一种全新的可能.它让你可以在 Node 侧,直接拿到 Chromium 侧的完整 Cookie.然后你就可以使用 Node 的方式,以最精简的代码,最符合直觉的方式来处理文件上传.

好吧,借题插一句:我曾经处理过一个 XML 文件解析的需求.当时搜了各种 Node 库,都没太好使的,后来我是直接在 render process 中,直接用 html 的dom 接口去读取和解析的 xml 文件! -- 当时,被自己的机智惊呆了! 讲真, 使用 Electron 来开发桌面 App,你真正需要考虑的不是如何实现某个需求,而是如何以一种更优雅的方式来实现需求! 没有做不到,只有不敢想啊!!!

一个简单的实例: 实现开源中国用户头像自动更换

为了完整演示这一技术可能涉及的特定问题及其解决方案,我们就从真实环境中来构造一个需求: 实现开源中国用户头像自动更换. 当然,是不需要 OSC 的研发改任何代码的前提下!!!

安装 electron

安装,建议使用稳定版本 1.3.x 系列的,可能需要 科学上网,才能安装.基础的快速入门教程,参考: https://electron.atom.io/docs/tutorial/quick-start/

npm i electron@1.3.15 -g

jquery 无效问题.

我们把入门示例中的url换为 osc 官方的域名:

win.loadURL("https://www.oschina.net/")

cd 到项目目录,执行:

electron .

此时 electron 就运行起来了,不过当你切换到登录页后, devtool 窗口,应该会报错:

Uncaught ReferenceError: $ is not defined

错误的原因,可以参考: https://github.com/electron/electron/issues/254 不过解决方式,都不适用于这里的场景.因为我不能直接修改 OSC 网站源码.

不过 Electron 创建窗口时,提供了一个 preload 参数,允许注入一个 js 文件到网页上下中:

  win = new BrowserWindow({width: 1300, height: 720, webPreferences:{
preload:path.resolve("./osc-preload.js")
}})

然后,我们可以重写在注入的js中,重写 window 的 $ 和 jquery 属性的 getter 方法:

Object.defineProperties(window,{
"$":{
get: function () {
return require("jquery")
},
configurable : true
},
"jQuery":{
get: function () {
return require("jquery");
},
configurable : true
}
});

此处,之所以是重写getter,而不是直接赋值,是因为 jquery 依赖于特定的 dom 结构,但是预注入的js文件在执行时,是没有任何 dom 结构的.注入的js文件,执行时机非常早,甚至早过 dom 或任意其他css/js 文件 的加载或渲染.

有关重写 getter 和 setter 方法的技术细节,大家可以参考这里: https://segmentfault.com/a/1190000003882976

当然,此处需要我们先在本地项目中,先安装 jquery 依赖,从 osc 源码中分析出,它用的 2.2.4 版本,我们最好也安装对应版本:

npm i jquery@2.2.4 --save

找到头像上传接口

这个很容易,只要通过 ctrl/cmd + alt + i 快捷键打开devtool,然后自己替换下头像,找到那个 ajax 请求就可以了.

我们注意到 formData 主要有 img 和 user_code 两部分构成.

构造 img 字段

其实就是一个图片的 base 编码, Node 搞这个,轻轻松啊.

先安装一个工具库: base64-img

npm install base64-img --save

然后:

/* 我们有足够丰富的方式来获取或计算图片的路径,此处默认采用的方式就是:
当前目录下的 test.jpeg 图片.
另外,此处文件注意使用 jpeg 后缀.这要是 OSC 本身的限制.*/
const imgPath = path.resolve(__dirname,"./test.jpeg") /* 此处,将文件转换为 base64,只是因为 osc 的头像变更接口,设计如此!! */
const imgData = base64Img.base64Sync(imgPath)

分析并获取 user_code

你要相信,任何在 Electron 打开的网站,即时你不是网站的拥有者,也可以获取比网站的前端研发人员更多的信息. Electron 的机制使然.

只要在 devtool 的源码区域,简单搜索下,就很容易发现 user_code 的来源.压缩后的源码,如果看着不输入,可以点击源码视图区左下角的格式化按钮 {} 格式化一下.

所以获取 user_code 的代码,就是:

    g = $("val[data-name=user_code]").data("value"),

获取完整 Cookie

Electron 有想成的 API 可以用.Cookie 操作的文档,参见:https://electron.atom.io/docs/api/cookies/

const { session } = require('electron').remote

session.defaultSession.cookies.get({
url: window.location.href
}, (error, cookies) => {
console.log(cookies)
})

使用 request 库发送头像更新请求

request 的详细文档,参考: https://www.npmjs.com/package/request.需要先安装依赖:

npm i request --save
const request = require("request")

request({
url: `https://my.oschina.net/action/user/save_portrait_new`,
method: "POST",
formData: {
img: imgData,
user_code: userCode
},
headers: {
"cookie": cookieStr
}
}, function(err, response, body) {
console.log(err, response, body) const {error} = JSON.parse(body)
if ( ! error) {
window.location.reload()
}
})

实例完整源码

感兴趣的童鞋,记得 Star 关注下 !!!

https://github.com/ios122/demo-electron-share-cookie

关于使用本地代理服务器获取完整 cookie 的思路

这个思考,主要是基于当期 App 的现状 -- 已经有了一个用于加速静态资源访问速度的用作缓存功能的本地代理服务器,还有就是当时也对 Electron 的 session 和 cookie 的接口,不太熟悉, 而采用的临时措施.但毕竟可用,顺便说下.其中的本地代理服务器部分,还是有一定参考价值的.

关于本地代理服务器,大家可以看下 微信小程序开发工具,会看到它的网络请求的 remote address 都是本地地址,很明显它加了本地代理服务器.

本地代理服务器本身,可以使用 https://www.npmjs.com/package/http-proxy 这个库.

Electron 想要支持设置网络代理,主要是用到 https://electron.atom.io/docs/api/session/#sessetproxyconfig-callback 接口.唯一主要注意的是,这个接口必须在 main process 调用,才会生效.

使用本地代理服务器获取完整 cookie 的思路是: 约定某个url路径,比如 /-fetch-all-cookies 为获取 cookie 的路径 --> 前端发送 ajax 请求到 /-fetch-all-cookies --> 本地代理服务器,拦截到请求,如果发现路径是 /-fetch-all-cookies,就把当次请求的 header 中的cookie 部分,作为返回值返回 --> 前端获取到完整 cookie --> 前端调用 request 库,上传文件.

虽然不是最优最简方案,但或许可以算得上是 脑洞开的最大的方案!!! O(∩_∩)O哈哈哈~

参考链接:

现在,以编程方式在 Electron 中上传文件,是非常简单的!的更多相关文章

  1. 【解决】AWS服务控制台中上传文件失败

    使用IE 11,在 AWS Services Console 中不管是 S3 还是 Elastic Beanstalk 的页面中上传页面都会失败,提示信息如下: A problem occurred ...

  2. ASP.Net中上传文件的几种方法

    在做Web项目时,上传文件是经常会碰到的需求.ASP.Net的WebForm开发模式中,封装了FileUpload控件,可以方便的进行文件上传操作.但有时,你可能不希望使用ASP.Net中的服务器控件 ...

  3. element-ui中上传文件upload

    <el-upload class="upload-demo" name="targetFile" ref="upload" :with ...

  4. vue中上传文件之multipart/form-data

    首先在项目里用了拦截器的,由于拦截器会将传递的参数转成对象,所以你i提交的时候会发现multipart/form-data或转变成application/json 其次关于input的文件上传是需要一 ...

  5. MVC中上传文件

    与asp.net中几乎一样,使用表单提交的方式上传文件(如果是使用了第三方插件的话,那么就另当别论) @{ ViewBag.Title = "Index"; Layout = nu ...

  6. 谈谈php中上传文件的处理

    这是一个表单的时代... 我们在浏览器中编辑自己的信息,会遇到上传头像:在文库中,我们会上传文档......到处存在“上传”这个词. php是最好的语言(其他语言的程序猿们不要打我...).php在处 ...

  7. Mac/Linux/Centos终端中上传文件到Linux云服务器

      1.mac上传文件到Linux服务器  scp 文件名 用户名@服务器ip:目标路径 如:scp /Users/test/testFile test@www.linuxidc.com:/test/ ...

  8. vue中上传文件相同文件名没反应

    vue项目中会遇到上传文件的需求,jquery会有一些插件很方便,如果不使用插件网上的方法没有太容易的而且很多是原生JS或者基于jQuery操作dom结构的.那么在vue项目中如何实现呢,还有如何模拟 ...

  9. Java中上传文件和表单数据提交如何保持数据的一致性?

    学生申请学科竞赛活动,表单中有学科竞赛的申报信息和部分附件,需要做到将上传文件和表单数据提交保持一致性. 将上传文件和插入表单数据放到事务汇总去处理,由于表单的数据我们可以控制,但是上传的文档不好控制 ...

随机推荐

  1. JS中的函数传参

    前言: 函数分为有参有返回值,有参无返回值,无参无返回值,无参有返回值:那么对于无参数的函数你想使用函数的调用怎么办呢?如果你想封装一个代码,实现多种功能,但是形参大于实参或者实参大于形参又该如何?本 ...

  2. Java代码编写规范(不是标准规范,自行整理,无须纠结)

    最近回过头来给以前的项目增加功能,发现之前写的注释非常不全,代码也非常的不整洁,有些地方写的''窝七八烂的,看着很不舒服:又恰好经理最近也经常跟我提起代码规范,我们就讨论了一下代码规范的重要性和必要性 ...

  3. Elasticsearch索引和文档操作

    列出所有索引 现在来看看我们的索引 GET /_cat/indices?v 响应 health status index uuid pri rep docs.count docs.deleted st ...

  4. Facebook开源Zstandard新型压缩算法代替Zlib 简单使用

    简介 Zstandard(缩写为Zstd)是由Facebook的Yann Collet开发的一个无损数据压缩算法.Zstandard在设计上与DEFLATE(.zip.gzip)算法有着差不多的压缩比 ...

  5. SQL中创建外键约束

    alter table 表名 add constraint 外键约束名 foreign key(列名) references 引用外键表(列名)

  6. Mybatis中javaType和jdbcType对应和CRUD例子

    JDBC Type Java Type CHAR String VARCHAR String LONGVARCHAR String NUMERIC java.math.BigDecimal DECIM ...

  7. ionic中应用sass

    在学习ionic过程中看到sass,总结了一下基本用法和问题解决办法1.首先需要一个ionic项目,并执行下面的命令ionic start CustomSass blank && cd ...

  8. oracle表空间自增长

    方式一:通过修改oracle database control 修改 第一步,点击开始--所有程序--Oracle - OraDb11g_home1--Database Control 第二步,通过g ...

  9. 【转载】Windows系统下删除ubuntu

    原始日期:2013-11-02 15:51 以windows7为例:   用MbrFix.exe修复MBR 卸载Windows/Linux双系统中的Ubuntu1.如果你有Windows系统安装盘/启 ...

  10. kubernetes 概览

    kubernetes 核心数据 (存储于ETCD 有数据变化通知的功能(watch-dog)) kubernetes 设计综述 (中英文对照)(http://www.oschina.net/trans ...