Skip to content

Commit 9a8e809

Browse files
authored
Add support for ZIP file attachments. (#222)
* zip * ZipFile ↦ ZipArchiveEntry * files ↦ fileNames * archive.filenames * better not found error * only include files in filenames
1 parent 602876c commit 9a8e809

File tree

2 files changed

+56
-9
lines changed

2 files changed

+56
-9
lines changed

src/fileAttachment.js

Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {require as requireDefault} from "d3-require";
22
import sqlite, {SQLiteDatabaseClient} from "./sqlite.js";
3+
import jszip from "./zip.js";
34

45
async function remote_fetch(file) {
56
const response = await fetch(await file.url());
@@ -14,15 +15,9 @@ async function dsv(file, delimiter, {array = false, typed = false} = {}) {
1415
: (array ? d3.csvParseRows : d3.csvParse))(text, typed && d3.autoType);
1516
}
1617

17-
class FileAttachment {
18-
constructor(url, name) {
19-
Object.defineProperties(this, {
20-
_url: {value: url},
21-
name: {value: name, enumerable: true}
22-
});
23-
}
24-
async url() {
25-
return (await this._url) + "";
18+
class AbstractFile {
19+
constructor(name) {
20+
Object.defineProperty(this, "name", {value: name, enumerable: true});
2621
}
2722
async blob() {
2823
return (await remote_fetch(this)).blob();
@@ -62,6 +57,20 @@ class FileAttachment {
6257
const db = new SQL.Database(new Uint8Array(buffer));
6358
return new SQLiteDatabaseClient(db);
6459
}
60+
async zip() {
61+
const [JSZip, buffer] = await Promise.all([jszip(requireDefault), this.arrayBuffer()]);
62+
return new ZipArchive(await JSZip.loadAsync(buffer));
63+
}
64+
}
65+
66+
class FileAttachment extends AbstractFile {
67+
constructor(url, name) {
68+
super(name);
69+
Object.defineProperty(this, "_url", {value: url});
70+
}
71+
async url() {
72+
return (await this._url) + "";
73+
}
6574
}
6675

6776
export function NoFileAttachments(name) {
@@ -78,3 +87,38 @@ export default function FileAttachments(resolve) {
7887
{prototype: FileAttachment.prototype} // instanceof
7988
);
8089
}
90+
91+
export class ZipArchive {
92+
constructor(archive) {
93+
Object.defineProperty(this, "_", {value: archive});
94+
this.filenames = Object.keys(archive.files).filter(name => !archive.files[name].dir);
95+
}
96+
file(path) {
97+
const object = this._.file(path += "");
98+
if (!object || object.dir) throw new Error(`file not found: ${path}`);
99+
return new ZipArchiveEntry(object);
100+
}
101+
}
102+
103+
class ZipArchiveEntry extends AbstractFile {
104+
constructor(object) {
105+
super(object.name);
106+
Object.defineProperty(this, "_", {value: object});
107+
Object.defineProperty(this, "_url", {writable: true});
108+
}
109+
async url() {
110+
return this._url || (this._url = this.blob().then(URL.createObjectURL));
111+
}
112+
async blob() {
113+
return this._.async("blob");
114+
}
115+
async arrayBuffer() {
116+
return this._.async("arraybuffer");
117+
}
118+
async text() {
119+
return this._.async("text");
120+
}
121+
async json() {
122+
return JSON.parse(await this.text());
123+
}
124+
}

src/zip.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default async function jszip(require) {
2+
return await require("[email protected]/dist/jszip.min.js");
3+
}

0 commit comments

Comments
 (0)