react+electron搭建七牛云上传图片桌面端

咋想的

每次上传图到七牛云,都需要登录七牛云找到仓库,上传。步骤有些繁琐。所以自己做个上传的小工具。

  • 自己画的原型图————简单、明了、好看
    七牛云上传

    步骤

  • 1、拖拽和选择图片,获取图片信息
  • 2、获取上传token
  • 3、点击上传
  • 4、包装electron
    —— 简简单单的四步

搭建

  • 1、create-react-app image-updata搭建一个名字为image-updata的项目
    这一步有必要且先执行这一步
    这时候yarn start就可以看到react页面里
    react页面
  • 2、yarn eject || npm run eject 可以显示出webpack配置文件 (修改了启动端口,默认3000 —— 不改也行,修改了静态文件打包路径,改成了相对路径 –不改也行。。。)
    • 修改启动端口
      修改scripts/start.js 的DEFAULT_PORT (scripts文件夹没有?执行yarn eject)
      修改端口号
    • 修改静态文件打包路径
      在yarn build 的时候build文件夹下的index.html里引入文件都用的是绝对路径,会找不到,—所以可以修改打包路径
      修改config/paths.js 的 getServedPage方法里的 servedUrl这块’/‘改成’./‘
      修改打包静态路径
    • 在执行yarn eject之后可能会报错,一般是缺少包,缺啥补啥就好*
      webpack报错
      yarn add @babel/helper-create-regexp-features-plugin

开始写页面

  • 这里就比较简单,就一个页面所以把App.js改改就好
  • 一开始是这样的
    一开始
  • 简单改改之后(结构就这样的)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
     <div className="App">
    <div className="content" key="content">
    <div className="set-button">
    <span onClick={()=>{}}>上传</span>
    </div>
    <div className="select-content">
    <div className="into">
    <ul>
    <li>图片名称</li>
    </ul>
    <p>将图片拖入</p>
    <p style={{marginTop:'30px',paddingTop:0}}>100%</p>
    <p style={{marginTop:'30px',paddingTop:0}}>提示文字</p>
    </div>
    <div className="select-image-button">
    <input className="iamge-file" type="file" onChange={()=>{}}/>
    <span>选择图片</span>
    </div>
    </div>
    </div>
    <div className="updata-url" key="updata-url">
    <p>图片url:</p>
    <p className="image-url">图片地址</p>
    <p className="copy" onClick={this.urlCopy}>点击复制</p>
    <input
    className="copy_content"
    type="text"
    value=""
    onChange={()=>{}}
    style={{position: 'absolute','top':0,'left': 0,'opacity': 0,'zIndex':'-10'}}
    />
    </div>
    </div>
    • 样式修改App.css,我就不多说了
  • 这时候的页面
    这时候的页面

开始步骤 1 拖拽和选择图片获取图片信息

  • 1、拖拽图片放下的时候获取图片信息
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    pullImageFunct(){ // 拖拽图片获取
    this.dropEle = document.querySelector('.into'); // 监听的鼠标放下区域
    const thse = this;
    this.dropEle.addEventListener("dragenter", function (e) { //文件拖拽进
    e.preventDefault();
    e.stopPropagation();
    }, false);
    this.dropEle.addEventListener("dragover", function (e) { //文件拖拽在悬浮
    e.preventDefault();
    e.stopPropagation();
    }, false);
    this.dropEle.addEventListener("dragleave", function (e) {//文件拖拽离开
    e.preventDefault();
    e.stopPropagation();
    }, false);
    this.dropEle.addEventListener("drop", function (e) {//文件拖拽放下
    e.preventDefault();
    e.stopPropagation();
    // 处理拖拽文件的逻辑
    var df = e.dataTransfer;
    for(var i = 0; i < df.items.length; i++) {
    var item = df.items[i];
    if(item.kind === "file" && item.webkitGetAsEntry().isFile) {
    var file = item.getAsFile();
    thse.setImageInfo(file); // 统一设置文件信息
    }
    }
    });
    }
    • 要禁止拖进、悬浮、拖出事件,否则会跳转
      1
      2
      3
      componentDidMount(){
      this.pullImageFunct();
      }
    • 拖拽事件需要监听
  • 2、文件选择
    1
    2
    3
    4
    5
    6
    7
    8
    inputFunct(e){ // 选择图片
    const path = e.target.files[0]['path']; // 文件路径
    const type = e.target.files[0]['type'];
    // 正则去文件名字 /.*\/([^\/]+)\..+/
    const rgx = /.*\/([^\/]+)/;
    const name = rgx.exec(path)[1];
    this.setImageInfo({path,name,type}) // 统一设置文件信息
    }
  • 需要在input的change事件执行
  • 统一设置文件信息
    1
    2
    3
    4
    5
    6
    7
    8
    setImageInfo(info){ // 设置上传参数
    this.setState({
    imagePath:info.path,
    selectImgName:info.name,
    imageType:info.type,
    showMessage:false,
    })
    }
  • 这个时候就可以拿到文件的path、type、name。第一步完成

开始步骤 2 获取上传token

七牛上传的时候需要token,获取tokan,需要accessKey,secretKey,都可以在七牛找到
可以在密钥管理中找到

密钥管理

  • 获取token
    yarn add qiniu 安装qnniu
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import nodeQiniu from 'qiniu';
const accessKey = ''; // 你的密钥
const secretKey = ''; // 你的密钥
getToKen(){ // 获取token
return new Promise((res,rej)=>{
const bucket = 'image_list'; // 这是你的图片仓库名
let mac = new nodeQiniu.auth.digest.Mac(accessKey, secretKey);
let options = {
scope: bucket,
expires: 3600 * 24 // 密钥过期时间
};
let putPolicy = new nodeQiniu.rs.PutPolicy(options);
let uploadToken= putPolicy.uploadToken(mac);
if(uploadToken){
res(uploadToken); // 你的上传token
}else{
rej();
}
});
}
  • 这是好你的密钥和图片仓库名,就可以拿到密钥

开始步骤 3 上传图片

qiniu.upload 是七牛上传图片的api,需要blob格式的图片
图片设置blob,在点击上传click执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
imageBlob(){ //获取图片 blob
const ther = this;
const {imagePath} = this.state; //文件路径
if(!imagePath){
this.setState({
showMessage:true,
message:'未获取到图片'
})
return;
}
const xhr = new XMLHttpRequest(); // ajax请求图片
xhr.open("get", `file://${imagePath}`, true);
xhr.responseType = "blob";
xhr.onload = async function () {
if (this.status === 200) {
//得到一个blob对象
var blob = this.response; // 图片blob
const token = await ther.getToKen(); // 获取token
ther.imageUpData({token,blob}) // 上传图片
}
}
xhr.send();
}

上传
yarn add qiniu-js 安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
imageUpData({token,blob}){ // 上传图片 token,blob
const {selectImgName,imageType} = this.state; //图片名称和图片type
const ther = this;
const putExtra = { // 上传需要的参数
fname: "",
params: {},
mimeType: [imageType]
};
const config = { // 上传需要的参数
useCdnDomain: true,
region: qiniu.region.z2
};
const observable = qiniu.upload(blob,selectImgName,token,putExtra,config);
observable.subscribe({
next(res){
ther.setState({
progress:res.total.percent.toFixed(2) // 进度
})
},
error(err){ // 失败
if(err.code == 401){
ther.setState({
showMessage:true,
message:'密钥错误,检查密钥'
})
}else{
ther.setState({
showMessage:true,
message:err.message
})
}
},
complete(res){ // 成功
ther.setState({
loadUrl:res.key,
progress:0,
isSuccess:true,
message:'成功--点击下方复制url'
})
}
})
}
  • 这时候就可以上传图片了
  • 点击复制
  • 需要设置个图片仓库中使用的域名
    1
    2
    3
    4
    5
    6
    7
    8
    9
    const host = ''; //你的图片仓库使用的域名 
    urlCopy(){ //点击复制url
    const {loadUrl} = this.state; // 上传后返回的路径
    const inputElement = document.querySelector('.copy_content'); // 点击复制只能在input上
    inputElement.value = host+loadUrl;
    inputElement.select(); // 必须
    document.execCommand("Copy"); // 浏览器提供
    global.alert('复制成功');
    }

开始步骤 4 包装electron

npm install electron --save-dev 安装 这一步会很慢,可以转taobao的源

  • 在根目录创建 main.js 和 renderer.js
    mainjs
  • main.js 本地测试注意端口号
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    const { app, BrowserWindow } = require('electron');
    const path = require('path');
    let mainWindow = null;
    //判断命令行脚本的第二参数是否含--debug
    const debug = /--debug/.test(process.argv[2]);
    function makeSingleInstance () {
    if (process.mas) return;
    app.requestSingleInstanceLock();
    app.on('second-instance', () => {
    if (mainWindow) {
    if (mainWindow.isMinimized()) mainWindow.restore()
    mainWindow.focus()
    }
    })
    }
    function createWindow () {
    mainWindow = new BrowserWindow({
    width: 300,
    height: 500,
    webPreferences: {
    javascript: true,
    plugins: true,
    nodeIntegration: true, // 是否集成 Nodejs
    webSecurity: false,
    preload: path.join(__dirname, './renderer.js') // 但预加载的 js 文件内仍可以使用 Nodejs 的 API
    }
    })
    mainWindow.loadURL("http://localhost:8403/"); //本地测试注意端口号
    // mainWindow.loadURL(path.join('file://',__dirname,'/build/index.html')); //打包使用

    //接收渲染进程的信息
    const ipc = require('electron').ipcMain;
    ipc.on('min', function () {
    mainWindow.minimize();
    });
    ipc.on('max', function () {
    mainWindow.maximize();
    });
    ipc.on("login",function () {
    mainWindow.maximize();
    });
    mainWindow.on('closed', () => {
    mainWindow = null
    })
    }
    makeSingleInstance();
    //app主进程的事件和方法
    app.on('ready', () => {
    createWindow();
    });
    app.on('window-all-closed', () => {
    if (process.platform !== 'darwin') {
    app.quit()
    }
    });
    app.on('activate', () => {
    if (mainWindow === null) {
    createWindow();
    }
    });
    module.exports = mainWindow;
  • renderer.js 必须
    1
    global.electron = require('electron');
  • 修改package.json*
  • 添加main.js
    main.js
  • 设置启动命令
    "start": "node scripts/start.js | electron . --debug",

这个时候关闭服务,在启动服务yarn start,就可以在electron中启动了
*
有时候会出现端口占用的情况**
mac 可以 lsof -i :端口号 看端口占中 kill -9 PID 关闭进程
*
*

  • 最后一步打包
    npm install electron-packager --save-dev 安装electron 打包工具
    • package.json 添加打包命令
      "package": "electron-packager ./"
      打包命令
    • 修改main.js中的mainWindow.loadURL
      mainWindow.loadURL(path.join('file://',__dirname,'/build/index.html'))
  • 执行 yarn package 会在根目录创建一个打包文件可以直接使用
  • 可以修改yarn package命令定制打包名称、地址、打包系统、图标
  • 什么都没设置会根据本地系统生成打包文件(我在mac上可以)
    打包的文件

github位置

  • 到此结束