Skip to content

基础强化方法推荐

简介

目前提供两个方法:

  • openFile:打开文件
  • downloadFile:下载文件

如果使用浏览器原生的方案,会导致插件在选择文件或下载文件时,插件页面被隐藏,使用上面的两个方法,插件页面就不会失焦。

使用方法

openFile

此方法可以通过api的形式选择一个文件。例如,我要选择一张图片,并在图片文件夹

typescript
window.preload.openFile({
  title: '请选择一张图片',
  // 图片文件夹
  defaultPath:  utools.getPath('Pictures'),
  buttonLabel: '选择',
  // 约束拓展名
  filters: [{
    name: '图片',
    // 约束拓展名为图片
    extensions: ['jpg', 'jpeg', 'png', 'gif', 'webp']
  }]
}).then(file => {
  // file为File对象
}).catch(e => {
  console.error("打开图片失败", e);
})

downloadFile

此方法可以把文件、链接、文字、base64格式的图片保存到本地。

  • 将一个base64格式的图片保存到本地
typescript
const base64 = 'data:image/png;base64,AAAAAAAAA==....'
window.preload.downloadFile(base64, '图片.png').then(path => {
  console.log('图片保存成功,保存到:', path);
});
  • 将一段文字保存到本地
typescript
const text = '测试问题'
const base64 = `data:text/plain;base64,${btoa(text)}`;
window.preload.downloadFile(base64, 'test.text').then(path => {
  console.log('文本保存成功,保存到:', path);
});
  • 将一个文件保存到本地
typescript
// 此处为接收到的文件对象
const file = new File();
window.preload.downloadFile(file, 'a.exe').then(path => {
  console.log('文件保存成功,保存到:', path);
});

使用前提

新建preload.js文件,并在plugin.json中指定preload.js

json
{
  "preload": "preload.js"
}

之后再preload.js中粘贴下面代码

preload.js

preload.js
javascript
const {readFile} = require('node:fs');
const {basename} = require('node:path');
const {URL} = require("node:url");
const https = require("node:https");
const http = require("node:http");

/**
 * 获取一个文件
 * @param options {options: {
 *     title?: string,
 *     defaultPath?: string,
 *     buttonLabel?: string,
 *     filters?: { name: string, extensions: string[] }[],
 *     properties?: Array<'openFile' | 'openDirectory' | 'multiSelections' | 'showHiddenFiles' | 'createDirectory' | 'promptToCreate' | 'noResolveAliases' | 'treatPackageAsDirectory' | 'dontAddToRecent'>,
 *     message?: string,
 *     securityScopedBookmarks?: boolean
 *   }} 参数
 * @return {Promise<File>} 返回blob对象
 */
function openFile(options) {
    return new Promise((resolve, reject) => {
        const paths = utools.showOpenDialog(options);
        const path = paths[0];
        if (path) {
            readFile(path, (err, data) => {
                if (err) {
                    reject(err);
                    return;
                }
                const blob = new Blob([data], {type: 'application/octet-stream'});
                resolve(new File([blob], basename(path)));
            })
        }
    })
}

/**
 * 从url下载一个文件
 * @param url 文件地址
 * @param path 保存的地址
 * @return {Promise<void>}
 */
function downloadFileFromUrl(url, path) {
    const file = fs.createWriteStream(path);
    const link = new URL(url);

    return new Promise((resolve, reject) => {
        file.on('finish', function () {
            file.close();
            resolve();
        }).on('error', e => {
            file.close();
            reject(e);
        });

        (link.protocol.startsWith("https") ? https : http).get(link, (response) => {
            response.pipe(file);
        }).on('error', (error) => {
            reject(error);
        }).end();
    })

}

/**
 * 下载一个文件
 * @param data {string | Blob | ArrayBuffer} 文件内容,可以使blob(file)或ArrayBuffer或者链接或者DATA URL
 * @param name {string} 文件名
 * @return {Promise<string>};
 */
async function downloadFile(data, name) {
    const target = utools.showSaveDialog({
        title: '请选择文件保存位置',
        buttonLabel: '保存',
        defaultPath: path.join(utools.getPath('downloads'), name),
        properties: ['createDirectory']
    });
    if (!target) {
        return Promise.reject(new Error("请选择文件保存位置"))
    }
    return new Promise(async (resolve, reject) => {
        if (data instanceof ArrayBuffer) {
            fs.writeFile(target, new Uint8Array(data), e => {
                if (e) {
                    return reject(e)
                }
                resolve(target);
            });
        } else if (data instanceof Blob) {
            const ab = await data.arrayBuffer();
            fs.writeFile(target, new Uint8Array(ab), e => {
                if (e) {
                    return reject(e)
                }
                resolve(target);
            });
        } else {
            // 如果是链接
            if (/^https?:\/\//.test(data)) {
                return downloadFileFromUrl(data, target)
                    .then(() => resolve(target))
                    .catch(reject);
            } else if (/^data:(.*?);base64,/.test(data)) {
                // 数据URL
                let byteCharacters = atob(
                    data.replace(/^data:(.*?);base64,/, "")
                );
                let byteNumbers = new Array(byteCharacters.length);
                for (let i = 0; i < byteCharacters.length; i++) {
                    byteNumbers[i] = byteCharacters.charCodeAt(i);
                }
                let byteArray = new Uint8Array(byteNumbers);
                fs.writeFile(target, byteArray, e => {
                    if (e) {
                        return reject(e)
                    }
                    resolve(target);
                });
            } else {
                reject(new Error("文件内容未知"))
            }
        }
    })
}

window.preload = {
    openFile, downloadFileFromUrl, downloadFile
}

vite-env.d.ts

如果您使用vite开发,可以将下面内容粘贴到vite-env.d.ts,如果您不是vite开发,可以在项目文件夹下创建文件index.d.ts ,将下面内容粘贴到文件内,即可获得代码提示。

vite-env.d.ts
typescript
interface OpenFileOption {
  title?: string,
  defaultPath?: string,
  buttonLabel?: string,
  filters?: { name: string, extensions: string[] }[],
  properties?: Array<'openFile' | 'openDirectory' | 'multiSelections' | 'showHiddenFiles' | 'createDirectory' | 'promptToCreate' | 'noResolveAliases' | 'treatPackageAsDirectory' | 'dontAddToRecent'>,
  message?: string,
  securityScopedBookmarks?: boolean
}

interface Window {
  preload: {
    /**
     * 打开一个文件,并返回File对象
     * @param options 参数
     * @return 返回File对象
     */
    openFile(options: OpenFileOption): Promise<File>,
    /**
     * 从url下载一个文件到指定目录
     * @param url 链接
     * @param path 要保存的文件路径,包含文件名
     */
    downloadFileFromUrl(url: string, path: string): Promise<void>,
    /**
     * 下载一个文件
     * @param data 文件内容,可以使blob(file)或ArrayBuffer或者链接或者DATA URL
     * @param name 文件名
     * @return 文件保存的路径
     */
    downloadFile(data: string | Blob | ArrayBuffer, name: string): Promise<string>
  }
}