ElectronでFileのOpenとSave


Electronでファイル操作する必要があったのでOpenとSaveの方法をメモ。

dialogの制御方法が知りたいのが主目的なので、ロジックはなし。
csvを読み込み表示。そのままsaveする。

## 仕様

下記のような感じ。

* openボタンを押すとファイオープンし、textareaに表示。
* saveでtextarea内の内容をfileにSave。

![スクリーンショット 2019-01-27 7.14.03.png](https://qiita-image-store.s3.amazonaws.com/0/55188/25b6408d-5954-b256-5ef1-ab7575e13807.png "スクリーンショット 2019-01-27 7.14.03.png")

>textarea内を変更しても内容はファイルには反映されない。それをやるにはVueとかReact使う。


## 実装


### package.json

エンドポイントはmain.jsとしました。

```json:package.json
{
  "name": "openclose",
  "version": "1.0.0",
  "description": "sample",
  "main": "main.js",
  "scripts": {
    "start": "electron ."
  },
  "devDependencies": {
    "electron": "^4.0.2"
  }
}
```

### main.js

ElectronのQuick Startのmain.jsとほぼ一緒。
起動時の表示としてindex.htmlを呼び出している。

```js:main.js
const { app, BrowserWindow } = require('electron')

let win

function createWindow() {

    //ウインドウの作成
    win = new BrowserWindow({ width: 800, height: 400 })

    //ウインドウに表示する内容
    win.loadFile('index.html')

    //デバッグ画面表示
    // win.webContents.openDevTools()

    //このウインドウが閉じられたときの処理
    win.on('closed', () => {
        win = null
    })
}

//アプリが初期化されたとき（起動されたとき）
app.on('ready', () => {
    createWindow()
})

//全ウインドウが閉じられたとき
app.on('window-all-closed', () => {
    if (process.platform !== 'darwin') {
        app.quit()
    }
})

//アクティブになったとき（MacだとDockがクリックされたとき）
app.on('activate', () => {
    if (win === null) {
        createWindow()
    }
})
```

### index.html

画面要素を配置。ロジックを実装するindex.jsとstyles.cssを呼び出している。

```html:index.html
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>sample</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <h3>File Open/Save</h3>
    <button id="openFile">Open</button>
    <button id="saveFile">Save</button>
    <br>
    <textarea id="preview"></textarea>
    <script src="index.js"></script>
</body>
</html>
```

### index.js

ここにいろいろ書く。やってることは、

* OpenDialogで開くファイル名(Path)を取得し、その情報を利用してファイルをオープン。
* SaveDialogで保存するファイル名（Path)を取得して、その情報を利用してファイルをSave。

```js:index.js
const fs = require('fs');
const { BrowserWindow, dialog } = require('electron').remote;

//html内の要素取得とリスナーの設定
document.querySelector("#openFile").addEventListener('click', () => {
    openFile();
})

document.querySelector("#saveFile").addEventListener('click', () => {
    saveFile();
})

const preview = document.getElementById('preview');

//openFileボタンが押されたとき（ファイル名取得まで）
function openFile() {
    const win = BrowserWindow.getFocusedWindow();
    dialog.showOpenDialog(
        win,
        {
            properties: ['openFile'],
            filters: [
                {
                    name: 'Document',
                    extensions: ['csv', 'txt']
                }
            ]
        },
        (fileNames) => {
            if (fileNames) {
                // alert(fileNames[0]);
                readFile(fileNames[0]); //複数選択の可能性もあるので配列となる。
            }
        }
    )
}

//指定したファイルを読み込む
function readFile(path) {
    fs.readFile(path, (error, data) => {
        if (error != null) {
            alert("file open error.");
            return;
        }
        preview.textContent = data.toString();
    })
}

//saveFileボタンが押されたとき
function saveFile() {
    const win = BrowserWindow.getFocusedWindow();
    dialog.showSaveDialog(
        win,
        {
            properties: ['openFile'],
            filters: [
                {
                    name: 'Documents',
                    extensions: ['csv', 'txt']
                }
            ]
        },
        (fileName) => {
            if (fileName) {
                const data = preview.textContent;
                console.log(data);
                writeFile(fileName, data);
            }
        }
    )
}

//fileを保存（Pathと内容を指定）
function writeFile(path, data) {
    fs.writeFile(path, data, (error) => {
        if (error != null) {
            alert("save error.");
            return;
        }
    })
}
```

### styles.css

まあ、おまけ程度ですが、いちおう。

```css:styles.css
#preview{
    margin-top: 20px;
    width: 300px;
    height: 100px;
}
```