summaryrefslogtreecommitdiff
path: root/js
diff options
context:
space:
mode:
authorBartek IwaƄczuk <biwanczuk@gmail.com>2019-03-28 04:29:36 +0100
committerRyan Dahl <ry@tinyclouds.org>2019-03-27 23:29:36 -0400
commit597ee38ef28d040cbf4d629cf3d2bd3e89a70a11 (patch)
treeed376d8951cd986beddf6a96341cbf47a2d22a11 /js
parentd0b6152f11800b0baac1ae68d2eef7bfcea13cb5 (diff)
Rewrite readFile and writeFile (#2000)
Using open/read/write
Diffstat (limited to 'js')
-rw-r--r--js/buffer.ts49
-rw-r--r--js/buffer_test.ts29
-rw-r--r--js/deno.ts9
-rw-r--r--js/files.ts180
-rw-r--r--js/files_test.ts36
-rw-r--r--js/io.ts11
-rw-r--r--js/read_file.ts37
-rw-r--r--js/write_file.ts70
8 files changed, 330 insertions, 91 deletions
diff --git a/js/buffer.ts b/js/buffer.ts
index c5be762f5..551e8c4d4 100644
--- a/js/buffer.ts
+++ b/js/buffer.ts
@@ -5,7 +5,7 @@
// https://github.com/golang/go/blob/master/LICENSE
//import * as io from "./io";
-import { Reader, Writer, ReadResult } from "./io";
+import { Reader, Writer, ReadResult, SyncReader, SyncWriter } from "./io";
import { assert } from "./util";
import { TextDecoder } from "./text_encoding";
import { DenoError, ErrorKind } from "./errors";
@@ -32,7 +32,7 @@ function copyBytes(dst: Uint8Array, src: Uint8Array, off = 0): number {
/** A Buffer is a variable-sized buffer of bytes with read() and write()
* methods. Based on https://golang.org/pkg/bytes/#Buffer
*/
-export class Buffer implements Reader, Writer {
+export class Buffer implements Reader, SyncReader, Writer, SyncWriter {
private buf: Uint8Array; // contents are the bytes buf[off : len(buf)]
private off = 0; // read at buf[off], write at buf[buf.byteLength]
@@ -126,11 +126,11 @@ export class Buffer implements Reader, Writer {
this.buf = new Uint8Array(this.buf.buffer, 0, len);
}
- /** read() reads the next len(p) bytes from the buffer or until the buffer
+ /** readSync() reads the next len(p) bytes from the buffer or until the buffer
* is drained. The return value n is the number of bytes read. If the
* buffer has no data to return, eof in the response will be true.
*/
- async read(p: Uint8Array): Promise<ReadResult> {
+ readSync(p: Uint8Array): ReadResult {
if (this.empty()) {
// Buffer is empty, reset to recover space.
this.reset();
@@ -145,11 +145,21 @@ export class Buffer implements Reader, Writer {
return { nread, eof: false };
}
- async write(p: Uint8Array): Promise<number> {
+ async read(p: Uint8Array): Promise<ReadResult> {
+ const rr = this.readSync(p);
+ return Promise.resolve(rr);
+ }
+
+ writeSync(p: Uint8Array): number {
const m = this._grow(p.byteLength);
return copyBytes(this.buf, p, m);
}
+ async write(p: Uint8Array): Promise<number> {
+ const n = this.writeSync(p);
+ return Promise.resolve(n);
+ }
+
/** _grow() grows the buffer to guarantee space for n more bytes.
* It returns the index where bytes should be written.
* If the buffer can't grow it will throw with ErrTooLarge.
@@ -226,6 +236,27 @@ export class Buffer implements Reader, Writer {
}
}
}
+
+ /** Sync version of `readFrom`
+ */
+ readFromSync(r: SyncReader): number {
+ let n = 0;
+ while (true) {
+ try {
+ const i = this._grow(MIN_READ);
+ this._reslice(i);
+ const fub = new Uint8Array(this.buf.buffer, i);
+ const { nread, eof } = r.readSync(fub);
+ this._reslice(i + nread);
+ n += nread;
+ if (eof) {
+ return n;
+ }
+ } catch (e) {
+ return n;
+ }
+ }
+ }
}
/** Read `r` until EOF and return the content as `Uint8Array`.
@@ -235,3 +266,11 @@ export async function readAll(r: Reader): Promise<Uint8Array> {
await buf.readFrom(r);
return buf.bytes();
}
+
+/** Read synchronously `r` until EOF and return the content as `Uint8Array`.
+ */
+export function readAllSync(r: SyncReader): Uint8Array {
+ const buf = new Buffer();
+ buf.readFromSync(r);
+ return buf.bytes();
+}
diff --git a/js/buffer_test.ts b/js/buffer_test.ts
index 90e171330..f07d9d65d 100644
--- a/js/buffer_test.ts
+++ b/js/buffer_test.ts
@@ -5,7 +5,7 @@
// https://github.com/golang/go/blob/master/LICENSE
import { assertEquals, test } from "./test_util.ts";
-const { Buffer, readAll } = Deno;
+const { Buffer, readAll, readAllSync } = Deno;
type Buffer = Deno.Buffer;
// N controls how many iterations of certain checks are performed.
@@ -193,6 +193,23 @@ test(async function bufferReadFrom() {
}
});
+test(async function bufferReadFromSync() {
+ init();
+ const buf = new Buffer();
+ for (let i = 3; i < 30; i += 3) {
+ const s = await fillBytes(
+ buf,
+ "",
+ 5,
+ testBytes.subarray(0, Math.floor(testBytes.byteLength / i))
+ );
+ const b = new Buffer();
+ b.readFromSync(buf);
+ const fub = new Uint8Array(testString.length);
+ await empty(b, s, fub);
+ }
+});
+
test(async function bufferTestGrow() {
const tmp = new Uint8Array(72);
for (let startLen of [0, 100, 1000, 10000, 100000]) {
@@ -226,3 +243,13 @@ test(async function testReadAll() {
assertEquals(testBytes[i], actualBytes[i]);
}
});
+
+test(function testReadAllSync() {
+ init();
+ const reader = new Buffer(testBytes.buffer as ArrayBuffer);
+ const actualBytes = readAllSync(reader);
+ assertEquals(testBytes.byteLength, actualBytes.byteLength);
+ for (let i = 0; i < testBytes.length; ++i) {
+ assertEquals(testBytes[i], actualBytes[i]);
+ }
+});
diff --git a/js/deno.ts b/js/deno.ts
index ed42dc89a..f7505fea4 100644
--- a/js/deno.ts
+++ b/js/deno.ts
@@ -6,12 +6,16 @@ export { chdir, cwd } from "./dir";
export {
File,
open,
+ openSync,
stdin,
stdout,
stderr,
read,
+ readSync,
write,
+ writeSync,
seek,
+ seekSync,
close,
OpenMode
} from "./files";
@@ -21,9 +25,12 @@ export {
ReadResult,
SeekMode,
Reader,
+ SyncReader,
Writer,
+ SyncWriter,
Closer,
Seeker,
+ SyncSeeker,
ReadCloser,
WriteCloser,
ReadSeeker,
@@ -31,7 +38,7 @@ export {
ReadWriteCloser,
ReadWriteSeeker
} from "./io";
-export { Buffer, readAll } from "./buffer";
+export { Buffer, readAll, readAllSync } from "./buffer";
export { mkdirSync, mkdir } from "./mkdir";
export {
makeTempDirSync,
diff --git a/js/files.ts b/js/files.ts
index 9ea378735..9da9fbe0a 100644
--- a/js/files.ts
+++ b/js/files.ts
@@ -1,20 +1,24 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
-import { Reader, Writer, Seeker, Closer, ReadResult, SeekMode } from "./io";
+import {
+ Reader,
+ Writer,
+ Seeker,
+ Closer,
+ ReadResult,
+ SeekMode,
+ SyncReader,
+ SyncWriter,
+ SyncSeeker
+} from "./io";
import * as dispatch from "./dispatch";
import * as msg from "gen/msg_generated";
import { assert } from "./util";
import * as flatbuffers from "./flatbuffers";
-/** Open a file and return an instance of the `File` object.
- *
- * (async () => {
- * const file = await Deno.open("/foo/bar.txt");
- * })();
- */
-export async function open(
+function reqOpen(
filename: string,
- mode: OpenMode = "r"
-): Promise<File> {
+ mode: OpenMode
+): [flatbuffers.Builder, msg.Any, flatbuffers.Offset] {
const builder = flatbuffers.createBuilder();
const filename_ = builder.createString(filename);
const mode_ = builder.createString(mode);
@@ -22,7 +26,10 @@ export async function open(
msg.Open.addFilename(builder, filename_);
msg.Open.addMode(builder, mode_);
const inner = msg.Open.endOpen(builder);
- const baseRes = await dispatch.sendAsync(builder, msg.Any.Open, inner);
+ return [builder, msg.Any.Open, inner];
+}
+
+function resOpen(baseRes: null | msg.Base): File {
assert(baseRes != null);
assert(msg.Any.OpenRes === baseRes!.innerType());
const res = new msg.OpenRes();
@@ -32,16 +39,40 @@ export async function open(
return new File(rid);
}
-/** Read from a file ID into an array buffer.
+/** Open a file and return an instance of the `File` object
+ * synchronously.
*
- * Resolves with the `ReadResult` for the operation.
+ * const file = Deno.openSync("/foo/bar.txt");
*/
-export async function read(rid: number, p: Uint8Array): Promise<ReadResult> {
+export function openSync(filename: string, mode: OpenMode = "r"): File {
+ return resOpen(dispatch.sendSync(...reqOpen(filename, mode)));
+}
+
+/** Open a file and return an instance of the `File` object.
+ *
+ * (async () => {
+ * const file = await Deno.open("/foo/bar.txt");
+ * })();
+ */
+export async function open(
+ filename: string,
+ mode: OpenMode = "r"
+): Promise<File> {
+ return resOpen(await dispatch.sendAsync(...reqOpen(filename, mode)));
+}
+
+function reqRead(
+ rid: number,
+ p: Uint8Array
+): [flatbuffers.Builder, msg.Any, flatbuffers.Offset, Uint8Array] {
const builder = flatbuffers.createBuilder();
msg.Read.startRead(builder);
msg.Read.addRid(builder, rid);
const inner = msg.Read.endRead(builder);
- const baseRes = await dispatch.sendAsync(builder, msg.Any.Read, inner, p);
+ return [builder, msg.Any.Read, inner, p];
+}
+
+function resRead(baseRes: null | msg.Base): ReadResult {
assert(baseRes != null);
assert(msg.Any.ReadRes === baseRes!.innerType());
const res = new msg.ReadRes();
@@ -49,16 +80,47 @@ export async function read(rid: number, p: Uint8Array): Promise<ReadResult> {
return { nread: res.nread(), eof: res.eof() };
}
-/** Write to the file ID the contents of the array buffer.
+/** Read synchronously from a file ID into an array buffer.
+ *
+ * Return `ReadResult` for the operation.
+ *
+ * const file = Deno.openSync("/foo/bar.txt");
+ * const buf = new Uint8Array(100);
+ * const { nread, eof } = Deno.readSync(file.rid, buf);
+ * const text = new TextDecoder.decode(buf);
*
- * Resolves with the number of bytes written.
*/
-export async function write(rid: number, p: Uint8Array): Promise<number> {
+export function readSync(rid: number, p: Uint8Array): ReadResult {
+ return resRead(dispatch.sendSync(...reqRead(rid, p)));
+}
+
+/** Read from a file ID into an array buffer.
+ *
+ * Resolves with the `ReadResult` for the operation.
+ *
+ * (async () => {
+ * const file = await Deno.open("/foo/bar.txt");
+ * const buf = new Uint8Array(100);
+ * const { nread, eof } = await Deno.read(file.rid, buf);
+ * const text = new TextDecoder.decode(buf);
+ * })();
+ */
+export async function read(rid: number, p: Uint8Array): Promise<ReadResult> {
+ return resRead(await dispatch.sendAsync(...reqRead(rid, p)));
+}
+
+function reqWrite(
+ rid: number,
+ p: Uint8Array
+): [flatbuffers.Builder, msg.Any, flatbuffers.Offset, Uint8Array] {
const builder = flatbuffers.createBuilder();
msg.Write.startWrite(builder);
msg.Write.addRid(builder, rid);
const inner = msg.Write.endWrite(builder);
- const baseRes = await dispatch.sendAsync(builder, msg.Any.Write, inner, p);
+ return [builder, msg.Any.Write, inner, p];
+}
+
+function resWrite(baseRes: null | msg.Base): number {
assert(baseRes != null);
assert(msg.Any.WriteRes === baseRes!.innerType());
const res = new msg.WriteRes();
@@ -66,21 +128,71 @@ export async function write(rid: number, p: Uint8Array): Promise<number> {
return res.nbyte();
}
-/** Seek a file ID to the given offset under mode given by `whence`.
+/** Write synchronously to the file ID the contents of the array buffer.
*
+ * Resolves with the number of bytes written.
+ *
+ * const encoder = new TextEncoder();
+ * const data = encoder.encode("Hello world\n");
+ * const file = Deno.openSync("/foo/bar.txt");
+ * Deno.writeSync(file.rid, data);
*/
-export async function seek(
+export function writeSync(rid: number, p: Uint8Array): number {
+ return resWrite(dispatch.sendSync(...reqWrite(rid, p)));
+}
+
+/** Write to the file ID the contents of the array buffer.
+ *
+ * Resolves with the number of bytes written.
+ *
+ * (async () => {
+ * const encoder = new TextEncoder();
+ * const data = encoder.encode("Hello world\n");
+ * const file = await Deno.open("/foo/bar.txt");
+ * await Deno.write(file.rid, data);
+ * })();
+ *
+ */
+export async function write(rid: number, p: Uint8Array): Promise<number> {
+ return resWrite(await dispatch.sendAsync(...reqWrite(rid, p)));
+}
+
+function reqSeek(
rid: number,
offset: number,
whence: SeekMode
-): Promise<void> {
+): [flatbuffers.Builder, msg.Any, flatbuffers.Offset] {
const builder = flatbuffers.createBuilder();
msg.Seek.startSeek(builder);
msg.Seek.addRid(builder, rid);
msg.Seek.addOffset(builder, offset);
msg.Seek.addWhence(builder, whence);
const inner = msg.Seek.endSeek(builder);
- await dispatch.sendAsync(builder, msg.Any.Seek, inner);
+ return [builder, msg.Any.Seek, inner];
+}
+
+/** Seek a file ID synchronously to the given offset under mode given by `whence`.
+ *
+ * const file = Deno.openSync("/foo/bar.txt");
+ * Deno.seekSync(file.rid, 0, 0);
+ */
+export function seekSync(rid: number, offset: number, whence: SeekMode): void {
+ dispatch.sendSync(...reqSeek(rid, offset, whence));
+}
+
+/** Seek a file ID to the given offset under mode given by `whence`.
+ *
+ * (async () => {
+ * const file = await Deno.open("/foo/bar.txt");
+ * await Deno.seek(file.rid, 0, 0);
+ * })();
+ */
+export async function seek(
+ rid: number,
+ offset: number,
+ whence: SeekMode
+): Promise<void> {
+ await dispatch.sendAsync(...reqSeek(rid, offset, whence));
}
/** Close the file ID. */
@@ -93,21 +205,41 @@ export function close(rid: number): void {
}
/** The Deno abstraction for reading and writing files. */
-export class File implements Reader, Writer, Seeker, Closer {
+export class File
+ implements
+ Reader,
+ SyncReader,
+ Writer,
+ SyncWriter,
+ Seeker,
+ SyncSeeker,
+ Closer {
constructor(readonly rid: number) {}
write(p: Uint8Array): Promise<number> {
return write(this.rid, p);
}
+ writeSync(p: Uint8Array): number {
+ return writeSync(this.rid, p);
+ }
+
read(p: Uint8Array): Promise<ReadResult> {
return read(this.rid, p);
}
+ readSync(p: Uint8Array): ReadResult {
+ return readSync(this.rid, p);
+ }
+
seek(offset: number, whence: SeekMode): Promise<void> {
return seek(this.rid, offset, whence);
}
+ seekSync(offset: number, whence: SeekMode): void {
+ return seekSync(this.rid, offset, whence);
+ }
+
close(): void {
close(this.rid);
}
diff --git a/js/files_test.ts b/js/files_test.ts
index 0decd9f00..f953946bc 100644
--- a/js/files_test.ts
+++ b/js/files_test.ts
@@ -163,6 +163,19 @@ testPerm({ read: true }, async function seekStart() {
assertEquals(decoded, "world!");
});
+testPerm({ read: true }, function seekSyncStart() {
+ const filename = "tests/hello.txt";
+ const file = Deno.openSync(filename);
+ // Deliberately move 1 step forward
+ file.readSync(new Uint8Array(1)); // "H"
+ // Skipping "Hello "
+ file.seekSync(6, Deno.SeekMode.SEEK_START);
+ const buf = new Uint8Array(6);
+ file.readSync(buf);
+ const decoded = new TextDecoder().decode(buf);
+ assertEquals(decoded, "world!");
+});
+
testPerm({ read: true }, async function seekCurrent() {
const filename = "tests/hello.txt";
const file = await Deno.open(filename);
@@ -176,6 +189,19 @@ testPerm({ read: true }, async function seekCurrent() {
assertEquals(decoded, "world!");
});
+testPerm({ read: true }, function seekSyncCurrent() {
+ const filename = "tests/hello.txt";
+ const file = Deno.openSync(filename);
+ // Deliberately move 1 step forward
+ file.readSync(new Uint8Array(1)); // "H"
+ // Skipping "ello "
+ file.seekSync(5, Deno.SeekMode.SEEK_CURRENT);
+ const buf = new Uint8Array(6);
+ file.readSync(buf);
+ const decoded = new TextDecoder().decode(buf);
+ assertEquals(decoded, "world!");
+});
+
testPerm({ read: true }, async function seekEnd() {
const filename = "tests/hello.txt";
const file = await Deno.open(filename);
@@ -186,6 +212,16 @@ testPerm({ read: true }, async function seekEnd() {
assertEquals(decoded, "world!");
});
+testPerm({ read: true }, function seekSyncEnd() {
+ const filename = "tests/hello.txt";
+ const file = Deno.openSync(filename);
+ file.seekSync(-6, Deno.SeekMode.SEEK_END);
+ const buf = new Uint8Array(6);
+ file.readSync(buf);
+ const decoded = new TextDecoder().decode(buf);
+ assertEquals(decoded, "world!");
+});
+
testPerm({ read: true }, async function seekMode() {
const filename = "tests/hello.txt";
const file = await Deno.open(filename);
diff --git a/js/io.ts b/js/io.ts
index 7f56187c1..dd3d12a6d 100644
--- a/js/io.ts
+++ b/js/io.ts
@@ -49,6 +49,10 @@ export interface Reader {
read(p: Uint8Array): Promise<ReadResult>;
}
+export interface SyncReader {
+ readSync(p: Uint8Array): ReadResult;
+}
+
// Writer is the interface that wraps the basic write() method.
// https://golang.org/pkg/io/#Writer
export interface Writer {
@@ -63,6 +67,9 @@ export interface Writer {
write(p: Uint8Array): Promise<number>;
}
+export interface SyncWriter {
+ writeSync(p: Uint8Array): number;
+}
// https://golang.org/pkg/io/#Closer
export interface Closer {
// The behavior of Close after the first call is undefined. Specific
@@ -85,6 +92,10 @@ export interface Seeker {
seek(offset: number, whence: SeekMode): Promise<void>;
}
+export interface SyncSeeker {
+ seekSync(offset: number, whence: SeekMode): void;
+}
+
// https://golang.org/pkg/io/#ReadCloser
export interface ReadCloser extends Reader, Closer {}
diff --git a/js/read_file.ts b/js/read_file.ts
index 3aeedec28..1ca52e276 100644
--- a/js/read_file.ts
+++ b/js/read_file.ts
@@ -1,29 +1,6 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
-import * as msg from "gen/msg_generated";
-import * as flatbuffers from "./flatbuffers";
-import { assert } from "./util";
-import * as dispatch from "./dispatch";
-
-function req(
- filename: string
-): [flatbuffers.Builder, msg.Any, flatbuffers.Offset] {
- const builder = flatbuffers.createBuilder();
- const filename_ = builder.createString(filename);
- msg.ReadFile.startReadFile(builder);
- msg.ReadFile.addFilename(builder, filename_);
- const inner = msg.ReadFile.endReadFile(builder);
- return [builder, msg.Any.ReadFile, inner];
-}
-
-function res(baseRes: null | msg.Base): Uint8Array {
- assert(baseRes != null);
- assert(msg.Any.ReadFileRes === baseRes!.innerType());
- const inner = new msg.ReadFileRes();
- assert(baseRes!.inner(inner) != null);
- const dataArray = inner.dataArray();
- assert(dataArray != null);
- return new Uint8Array(dataArray!);
-}
+import { open, openSync } from "./files";
+import { readAll, readAllSync } from "./buffer";
/** Read the entire contents of a file synchronously.
*
@@ -32,7 +9,10 @@ function res(baseRes: null | msg.Base): Uint8Array {
* console.log(decoder.decode(data));
*/
export function readFileSync(filename: string): Uint8Array {
- return res(dispatch.sendSync(...req(filename)));
+ const file = openSync(filename);
+ const contents = readAllSync(file);
+ file.close();
+ return contents;
}
/** Read the entire contents of a file.
@@ -42,5 +22,8 @@ export function readFileSync(filename: string): Uint8Array {
* console.log(decoder.decode(data));
*/
export async function readFile(filename: string): Promise<Uint8Array> {
- return res(await dispatch.sendAsync(...req(filename)));
+ const file = await open(filename);
+ const contents = await readAll(file);
+ file.close();
+ return contents;
}
diff --git a/js/write_file.ts b/js/write_file.ts
index 8c5c7ac0a..39372a27e 100644
--- a/js/write_file.ts
+++ b/js/write_file.ts
@@ -1,35 +1,7 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
-import * as msg from "gen/msg_generated";
-import * as flatbuffers from "./flatbuffers";
-import * as dispatch from "./dispatch";
-
-function req(
- filename: string,
- data: Uint8Array,
- options: WriteFileOptions
-): [flatbuffers.Builder, msg.Any, flatbuffers.Offset, Uint8Array] {
- const builder = flatbuffers.createBuilder();
- const filename_ = builder.createString(filename);
- msg.WriteFile.startWriteFile(builder);
- msg.WriteFile.addFilename(builder, filename_);
- // Perm is not updated by default
- if (options.perm !== undefined && options.perm !== null) {
- msg.WriteFile.addUpdatePerm(builder, true);
- msg.WriteFile.addPerm(builder, options.perm!);
- } else {
- msg.WriteFile.addUpdatePerm(builder, false);
- msg.WriteFile.addPerm(builder, 0o666);
- }
- // Create is turned on by default
- if (options.create !== undefined) {
- msg.WriteFile.addIsCreate(builder, !!options.create);
- } else {
- msg.WriteFile.addIsCreate(builder, true);
- }
- msg.WriteFile.addIsAppend(builder, !!options.append);
- const inner = msg.WriteFile.endWriteFile(builder);
- return [builder, msg.Any.WriteFile, inner, data];
-}
+import { stat, statSync } from "./stat";
+import { open, openSync } from "./files";
+import { chmod, chmodSync } from "./chmod";
/** Options for writing to a file.
* `perm` would change the file's permission if set.
@@ -53,7 +25,23 @@ export function writeFileSync(
data: Uint8Array,
options: WriteFileOptions = {}
): void {
- dispatch.sendSync(...req(filename, data, options));
+ if (options.create !== undefined) {
+ const create = !!options.create;
+ if (!create) {
+ // verify that file exists
+ statSync(filename);
+ }
+ }
+
+ const openMode = !!options.append ? "a" : "w";
+ const file = openSync(filename, openMode);
+
+ if (options.perm !== undefined && options.perm !== null) {
+ chmodSync(filename, options.perm);
+ }
+
+ file.writeSync(data);
+ file.close();
}
/** Write a new file, with given filename and data.
@@ -67,5 +55,21 @@ export async function writeFile(
data: Uint8Array,
options: WriteFileOptions = {}
): Promise<void> {
- await dispatch.sendAsync(...req(filename, data, options));
+ if (options.create !== undefined) {
+ const create = !!options.create;
+ if (!create) {
+ // verify that file exists
+ await stat(filename);
+ }
+ }
+
+ const openMode = !!options.append ? "a" : "w";
+ const file = await open(filename, openMode);
+
+ if (options.perm !== undefined && options.perm !== null) {
+ await chmod(filename, options.perm);
+ }
+
+ await file.write(data);
+ file.close();
}