【问题标题】:Electron - write file before open save dialog电子 - 在打开保存对话框之前写入文件
【发布时间】:2021-03-17 23:31:46
【问题描述】:

我正在使用电子开发应用程序。完成一些加密操作后,我需要向用户显示一个对话框来保存文件。我想给文件的文件名是一个随机哈希,但我也没有成功。我正在尝试使用此代码,但不会保存文件。我该如何解决这个问题?

const downloadPath = app.getPath('downloads')

ipcMain.on('encryptFiles', (event, data) => {
  let output = [];
  const password = data.password;
  data.files.forEach( (file) => {
    const buffer = fs.readFileSync(file.path);
    const dataURI = dauria.getBase64DataURI(buffer, file.type);
    const encrypted = CryptoJS.AES.encrypt(dataURI, password).toString();
    output.push(encrypted);
  })
  const filename = hash.createHash('md5').toString('hex');
  console.log(filename)
  const response = output.join(' :: ');
  dialog.showSaveDialog({title: 'Save encrypted file', defaultPath: downloadPath }, () => {
    fs.writeFile(`${filename}.mfs`, response, (err) => console.log(err) )  
  })
})

【问题讨论】:

  • 如果你自己为文件创建一个随机的文件名,为什么你还想在用户无法更改名称时显示保存对话框?
  • @AlexanderLeithner 使用保存对话框,用户将能够更改文件名。在代码中,不会创建随机文件名,我还需要修复它。

标签: javascript node.js vue.js electron


【解决方案1】:

您遇到的问题是由 Electron 的 UI 函数的异步特性引起的:它们不接受回调函数,而是返回 Promise。因此,您不必传入回调函数,而是处理 Promise 的解析。请注意,这仅适用于 Electron >= 版本 6。但是,如果您运行旧版本的 Electron,您的代码将是正确的——但是您应该真正更新到较新的版本(Electron v6 是一年多前发布的) .

像下面这样调整您的代码可以成为解决问题的起点。但是,由于您没有说明如何生成哈希(hash.createHash 来自哪里?;您是否忘记声明/导入hash?;您是否忘记传递任何消息字符串?;您是否使用hash作为 NodeJS 的 crypto 模块的别名?),(此时)无法调试为什么您没有从 console.log (filename) 获得任何输出(我假设您的意思是“在代码中,随机文件名将不会被创造”)。一旦您提供有关此问题的更多详细信息,我很乐意相应地更新此答案。

至于默认文件名:根据Electron documentation,您可以将文件路径传递给dialog.showSaveDialog (),为用户提供默认文件名。

您使用的文件类型扩展名实际上也应该与文件扩展名一起传递到保存对话框中。此外,将此文件扩展名作为过滤器传递到对话框中将阻止用户选择任何其他文件类型,这最终也是您当前正在执行的操作,方法是将其附加到文件名中。

此外,您可以使用 CryptoJS 生成文件名:给定一些任意字符串,这可能是随机字节,您可以这样做:filename = CryptoJS.MD5 ('some text here') + '.mfs'; 但是,请记住明智地选择输入字符串。 MD5 已被破坏,因此不应再用于存储机密信息——使用任何已知信息对您存储的文件加密至关重要(例如data.password)本质上是不安全的。互联网上有一些关于如何在 JavaScript 中创建随机字符串的很好的例子,还有this answer 在 SO 上。

考虑到所有这些问题,最终可能会得到以下代码:

const downloadPath = app.getPath('downloads'),
      path = require('path');

ipcMain.on('encryptFiles', (event, data) => {
  let output = [];
  const password = data.password;

  data.files.forEach((file) => {
    const buffer = fs.readFileSync(file.path);
    const dataURI = dauria.getBase64DataURI(buffer, file.type);
    const encrypted = CryptoJS.AES.encrypt(dataURI, password).toString();
    output.push(encrypted);
  })

  // not working:
  // const filename = hash.createHash('md5').toString('hex') + '.mfs';

  // alternative requiring more research on your end
  const filename = CryptoJS.MD5('replace me with some random bytes') + '.mfs';
  console.log(filename);

  const response = output.join(' :: ');
  dialog.showSaveDialog(
    {
      title: 'Save encrypted file',
      defaultPath: path.format ({ dir: downloadPath, base: filename }), // construct a proper path
      filters: [{ name: 'Encrypted File (*.mfs)', extensions: ['mfs'] }] // filter the possible files
    }
  ).then ((result) => {
    if (result.canceled) return; // discard the result altogether; user has clicked "cancel"
    else {
      var filePath = result.filePath;
      if (!filePath.endsWith('.mfs')) {
        // This is an additional safety check which should not actually trigger.
        // However, generally appending a file extension to a filename is not a
        // good idea, as they would be (possibly) doubled without this check.
        filePath += '.mfs';
      }
      fs.writeFile(filePath, response, (err) => console.log(err) )  
    }
  }).catch ((err) => {
    console.log (err);
  });
})

【讨论】:

  • 在电子文档中没有解释如何为对话框提供文件名和路径,我看到您使用了专用功能,我将按照您的建议修复代码。对于我现在使用的随机文件名 crypto 模块,我正在调用 randomBytes 并且它按预期工作。
  • 我使用的函数,path.format (),实际上只是为了构建一个你的应用程序运行的操作系统可以理解的路径(Windows 使用反斜杠作为路径分隔符,Linux、macOS、BSD 等. 使用正斜杠)。您可以只传递一个纯字符串,但这不能保证在任何地方都有效。如文档所述,文件名传递给 Electron 的方式是通过 options 对象的 defaultPath 参数(如您所见)。顺便说一句,使用randomBytes 是一个不错的决定。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-12-28
  • 1970-01-01
  • 2021-11-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多