feat(api): add writeTextFile and (path, contents, options) overload (#4228)

This commit is contained in:
Lucas Fernandes Nogueira 2022-05-29 06:10:41 -07:00 committed by GitHub
parent f685df399a
commit 3f998ca294
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 192 additions and 106 deletions

View File

@ -0,0 +1,5 @@
---
"api": patch
---
Renamed `writeFile` to `writeTextFile` but kept the original function for backwards compatibility.

View File

@ -0,0 +1,5 @@
---
"api": patch
---
Added `(path, contents[, options])` overload to the `writeTextFile` and `writeBinaryFile` APIs.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -3052,7 +3052,7 @@ dependencies = [
[[package]] [[package]]
name = "tauri" name = "tauri"
version = "1.0.0-rc.13" version = "1.0.0-rc.14"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"attohttpc", "attohttpc",
@ -3102,6 +3102,7 @@ dependencies = [
"tauri-utils", "tauri-utils",
"tempfile", "tempfile",
"thiserror", "thiserror",
"time",
"tokio", "tokio",
"url", "url",
"uuid 1.0.0", "uuid 1.0.0",
@ -3113,7 +3114,7 @@ dependencies = [
[[package]] [[package]]
name = "tauri-build" name = "tauri-build"
version = "1.0.0-rc.10" version = "1.0.0-rc.12"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"cargo_toml", "cargo_toml",
@ -3127,7 +3128,7 @@ dependencies = [
[[package]] [[package]]
name = "tauri-codegen" name = "tauri-codegen"
version = "1.0.0-rc.7" version = "1.0.0-rc.8"
dependencies = [ dependencies = [
"base64", "base64",
"brotli", "brotli",
@ -3136,7 +3137,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"regex", "regex",
"semver 1.0.7", "semver 1.0.9",
"serde", "serde",
"serde_json", "serde_json",
"sha2", "sha2",
@ -3148,7 +3149,7 @@ dependencies = [
[[package]] [[package]]
name = "tauri-macros" name = "tauri-macros"
version = "1.0.0-rc.7" version = "1.0.0-rc.8"
dependencies = [ dependencies = [
"heck 0.4.0", "heck 0.4.0",
"proc-macro2", "proc-macro2",
@ -3194,7 +3195,7 @@ dependencies = [
[[package]] [[package]]
name = "tauri-utils" name = "tauri-utils"
version = "1.0.0-rc.7" version = "1.0.0-rc.8"
dependencies = [ dependencies = [
"aes-gcm", "aes-gcm",
"brotli", "brotli",
@ -3209,7 +3210,7 @@ dependencies = [
"phf 0.10.1", "phf 0.10.1",
"proc-macro2", "proc-macro2",
"quote", "quote",
"semver 1.0.7", "semver 1.0.9",
"serde", "serde",
"serde_json", "serde_json",
"serde_with", "serde_with",

View File

@ -1,78 +1,77 @@
<script> <script>
import { readBinaryFile, readDir, Dir } from "@tauri-apps/api/fs"; import {
import { convertFileSrc } from "@tauri-apps/api/tauri"; readBinaryFile,
writeTextFile,
readDir,
Dir
} from '@tauri-apps/api/fs'
import { convertFileSrc } from '@tauri-apps/api/tauri'
export let onMessage; export let onMessage
export let insecureRenderHtml; export let insecureRenderHtml
let pathToRead = ""; let pathToRead = ''
let img; let img
function getDir() { function getDir() {
const dirSelect = document.getElementById("dir"); const dirSelect = document.getElementById('dir')
return dirSelect.value ? parseInt(dir.value) : null; return dirSelect.value ? parseInt(dir.value) : null
} }
function arrayBufferToBase64(buffer, callback) { function arrayBufferToBase64(buffer, callback) {
const blob = new Blob([buffer], { const blob = new Blob([buffer], {
type: "application/octet-binary", type: 'application/octet-binary'
}); })
const reader = new FileReader(); const reader = new FileReader()
reader.onload = function (evt) { reader.onload = function (evt) {
const dataurl = evt.target.result; const dataurl = evt.target.result
callback(dataurl.substr(dataurl.indexOf(",") + 1)); callback(dataurl.substr(dataurl.indexOf(',') + 1))
}; }
reader.readAsDataURL(blob); reader.readAsDataURL(blob)
} }
const DirOptions = Object.keys(Dir) const DirOptions = Object.keys(Dir)
.filter((key) => isNaN(parseInt(key))) .filter((key) => isNaN(parseInt(key)))
.map((dir) => [dir, Dir[dir]]); .map((dir) => [dir, Dir[dir]])
function read() { function read() {
const isFile = pathToRead.match(/\S+\.\S+$/g); const isFile = pathToRead.match(/\S+\.\S+$/g)
const opts = { const opts = {
dir: getDir(), dir: getDir()
}; }
const promise = isFile const promise = isFile
? readBinaryFile(pathToRead, opts) ? readBinaryFile(pathToRead, opts)
: readDir(pathToRead, opts); : readDir(pathToRead, opts)
promise promise
.then(function (response) { .then(function (response) {
if (isFile) { if (isFile) {
if (pathToRead.includes(".png") || pathToRead.includes(".jpg")) { if (pathToRead.includes('.png') || pathToRead.includes('.jpg')) {
arrayBufferToBase64(new Uint8Array(response), function (base64) { arrayBufferToBase64(new Uint8Array(response), function (base64) {
const src = "data:image/png;base64," + base64; const src = 'data:image/png;base64,' + base64
insecureRenderHtml('<img src="' + src + '"></img>'); insecureRenderHtml('<img src="' + src + '"></img>')
}); })
} else { } else {
const value = String.fromCharCode.apply(null, response); const value = String.fromCharCode.apply(null, response)
insecureRenderHtml( insecureRenderHtml(
'<textarea id="file-response"></textarea><button id="file-save">Save</button>' '<textarea id="file-response"></textarea><button id="file-save">Save</button>'
); )
setTimeout(() => { setTimeout(() => {
const fileInput = document.getElementById("file-response"); const fileInput = document.getElementById('file-response')
fileInput.value = value; fileInput.value = value
document document
.getElementById("file-save") .getElementById('file-save')
.addEventListener("click", function () { .addEventListener('click', function () {
writeFile( writeTextFile(pathToRead, fileInput.value, {
{ dir: getDir()
file: pathToRead, }).catch(onMessage)
contents: fileInput.value, })
}, })
{
dir: getDir(),
}
).catch(onMessage);
});
});
} }
} else { } else {
onMessage(response); onMessage(response)
} }
}) })
.catch(onMessage); .catch(onMessage)
} }
function setSrc() { function setSrc() {
@ -95,5 +94,5 @@
<button class="button" id="read">Read</button> <button class="button" id="read">Read</button>
<button class="button" type="button" on:click={setSrc}>Use as img src</button> <button class="button" type="button" on:click={setSrc}>Use as img src</button>
<img alt="file" bind:this={img}> <img alt="file" bind:this={img} />
</form> </form>

View File

@ -23,9 +23,9 @@
svelte-hmr "^0.14.7" svelte-hmr "^0.14.7"
"@tauri-apps/api@../../tooling/api/dist": "@tauri-apps/api@../../tooling/api/dist":
version "1.0.0-rc.3" version "1.0.0-rc.6"
dependencies: dependencies:
type-fest "2.12.2" type-fest "2.13.0"
"@zerodevx/svelte-json-view@0.2.0": "@zerodevx/svelte-json-view@0.2.0":
version "0.2.0" version "0.2.0"
@ -267,10 +267,10 @@ svelte@3.35.0:
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.35.0.tgz#e0d0ba60c4852181c2b4fd851194be6fda493e65" resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.35.0.tgz#e0d0ba60c4852181c2b4fd851194be6fda493e65"
integrity sha512-gknlZkR2sXheu/X+B7dDImwANVvK1R0QGQLd8CNIfxxGPeXBmePnxfzb6fWwTQRsYQG7lYkZXvpXJvxvpsoB7g== integrity sha512-gknlZkR2sXheu/X+B7dDImwANVvK1R0QGQLd8CNIfxxGPeXBmePnxfzb6fWwTQRsYQG7lYkZXvpXJvxvpsoB7g==
type-fest@2.12.2: type-fest@2.13.0:
version "2.12.2" version "2.13.0"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.12.2.tgz#80a53614e6b9b475eb9077472fb7498dc7aa51d0" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.13.0.tgz#d1ecee38af29eb2e863b22299a3d68ef30d2abfb"
integrity sha512-qt6ylCGpLjZ7AaODxbpyBZSs9fCI9SkL3Z9q2oxMBQhs/uyY+VD8jHA8ULCGmWQJlBgqvO3EJeAngOHD8zQCrQ== integrity sha512-lPfAm42MxE4/456+QyIaaVBAwgpJb6xZ8PRu09utnhPdWwcyj9vgy6Sq0Z5yNbJ21EdxB5dRU/Qg8bsyAMtlcw==
vite@^2.6.4: vite@^2.6.4:
version "2.6.14" version "2.6.14"

View File

@ -207,6 +207,11 @@ async function confirm(
}) })
} }
export type { DialogFilter, OpenDialogOptions, SaveDialogOptions } export type {
DialogFilter,
OpenDialogOptions,
SaveDialogOptions,
MessageDialogOptions
}
export { open, save, message, ask, confirm } export { open, save, message, ask, confirm }

View File

@ -96,6 +96,7 @@ export enum BaseDirectory {
interface FsOptions { interface FsOptions {
dir?: BaseDirectory dir?: BaseDirectory
// note that adding fields here needs a change in the writeBinaryFile check
} }
interface FsDirOptions { interface FsDirOptions {
@ -111,12 +112,14 @@ interface FsTextFileOption {
contents: string contents: string
} }
type BinaryFileContents = Iterable<number> | ArrayLike<number>
/** Options object used to write a binary data to a file. */ /** Options object used to write a binary data to a file. */
interface FsBinaryFileOption { interface FsBinaryFileOption {
/** Path to the file to write. */ /** Path to the file to write. */
path: string path: string
/** The byte array contents. */ /** The byte array contents. */
contents: Iterable<number> | ArrayLike<number> contents: BinaryFileContents
} }
interface FileEntry { interface FileEntry {
@ -174,6 +177,32 @@ async function readBinaryFile(
return Uint8Array.from(arr) return Uint8Array.from(arr)
} }
/**
* Writes a UTF-8 text file.
*
* @param path The file path.
* @param contents The file contents.
* @param options Configuration object.
* @returns A promise indicating the success or failure of the operation.
*/
async function writeTextFile(
path: string,
contents: string,
options?: FsOptions
): Promise<void>
/**
* Writes a UTF-8 text file.
*
* @param file The object containing the file path and contents.
* @param options Configuration object.
* @returns A promise indicating the success or failure of the operation.
*/
async function writeTextFile(
file: FsTextFileOption,
options?: FsOptions
): Promise<void>
/** /**
* Writes a UTF-8 text file. * Writes a UTF-8 text file.
* *
@ -181,15 +210,31 @@ async function readBinaryFile(
* @param options Configuration object. * @param options Configuration object.
* @returns A promise indicating the success or failure of the operation. * @returns A promise indicating the success or failure of the operation.
*/ */
async function writeFile( async function writeTextFile(
file: FsTextFileOption, path: string | FsTextFileOption,
options: FsOptions = {} contents?: string | FsOptions,
options?: FsOptions
): Promise<void> { ): Promise<void> {
if (typeof options === 'object') { if (typeof options === 'object') {
Object.freeze(options) Object.freeze(options)
} }
if (typeof file === 'object') { if (typeof path === 'object') {
Object.freeze(file) Object.freeze(path)
}
const file: FsTextFileOption = { path: '', contents: '' }
let fileOptions: FsOptions | undefined = options
if (typeof path === 'string') {
file.path = path
} else {
file.path = path.path
file.contents = path.contents
}
if (typeof contents === 'string') {
file.contents = contents ?? ''
} else {
fileOptions = contents
} }
return invokeTauriCommand({ return invokeTauriCommand({
@ -198,11 +243,37 @@ async function writeFile(
cmd: 'writeFile', cmd: 'writeFile',
path: file.path, path: file.path,
contents: Array.from(new TextEncoder().encode(file.contents)), contents: Array.from(new TextEncoder().encode(file.contents)),
options options: fileOptions
} }
}) })
} }
/**
* Writes a byte array content to a file.
*
* @param path The file path.
* @param contents The file contents.
* @param options Configuration object.
* @returns A promise indicating the success or failure of the operation.
*/
async function writeBinaryFile(
path: string,
contents: BinaryFileContents,
options?: FsOptions
): Promise<void>
/**
* Writes a byte array content to a file.
*
* @param file The object containing the file path and contents.
* @param options Configuration object.
* @returns A promise indicating the success or failure of the operation.
*/
async function writeBinaryFile(
file: FsBinaryFileOption,
options?: FsOptions
): Promise<void>
/** /**
* Writes a byte array content to a file. * Writes a byte array content to a file.
* *
@ -211,14 +282,31 @@ async function writeFile(
* @returns A promise indicating the success or failure of the operation. * @returns A promise indicating the success or failure of the operation.
*/ */
async function writeBinaryFile( async function writeBinaryFile(
file: FsBinaryFileOption, path: string | FsBinaryFileOption,
options: FsOptions = {} contents?: BinaryFileContents | FsOptions,
options?: FsOptions
): Promise<void> { ): Promise<void> {
if (typeof options === 'object') { if (typeof options === 'object') {
Object.freeze(options) Object.freeze(options)
} }
if (typeof file === 'object') { if (typeof path === 'object') {
Object.freeze(file) Object.freeze(path)
}
const file: FsBinaryFileOption = { path: '', contents: [] }
let fileOptions: FsOptions | undefined = options
if (typeof path === 'string') {
file.path = path
} else {
file.path = path.path
file.contents = path.contents
}
if (contents && 'dir' in contents) {
fileOptions = contents
} else {
// @ts-expect-error
file.contents = contents ?? []
} }
return invokeTauriCommand({ return invokeTauriCommand({
@ -227,7 +315,7 @@ async function writeBinaryFile(
cmd: 'writeFile', cmd: 'writeFile',
path: file.path, path: file.path,
contents: Array.from(file.contents), contents: Array.from(file.contents),
options options: fileOptions
} }
}) })
} }
@ -371,6 +459,7 @@ export type {
FsOptions, FsOptions,
FsDirOptions, FsDirOptions,
FsTextFileOption, FsTextFileOption,
BinaryFileContents,
FsBinaryFileOption, FsBinaryFileOption,
FileEntry FileEntry
} }
@ -379,7 +468,8 @@ export {
BaseDirectory as Dir, BaseDirectory as Dir,
readTextFile, readTextFile,
readBinaryFile, readBinaryFile,
writeFile, writeTextFile,
writeTextFile as writeFile,
writeBinaryFile, writeBinaryFile,
readDir, readDir,
createDir, createDir,

View File

@ -431,4 +431,4 @@ export type {
FetchOptions FetchOptions
} }
export { getClient, fetch, Body, Client, Response, ResponseType } export { getClient, fetch, Body, Client, Response, ResponseType, FilePart }

View File

@ -31,13 +31,7 @@
} }
return window.__TAURI__.fs return window.__TAURI__.fs
.writeFile( .writeTextFile('tauri-test.txt', contents, options)
{
path: 'tauri-test.txt',
contents: contents
},
options
)
.then(function (res) { .then(function (res) {
reply({ reply({
cmd: 'writeFile' + commandSuffix cmd: 'writeFile' + commandSuffix