Skip to content

Commit 64ea5eb

Browse files
authored
Merge pull request #3 from schl3ck/master
Support CommonJS, reading binary files and reading text with UTF-16 surrogate pairs
2 parents 8e70fbf + 29b6e87 commit 64ea5eb

File tree

4 files changed

+68
-48
lines changed

4 files changed

+68
-48
lines changed

tarball.js

Lines changed: 57 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
let tarball = {};
22

3+
if (typeof module === "object" && typeof module.exports === "object") {
4+
// CommonJS
5+
module.exports = tarball;
6+
} else if (typeof this === "object") {
7+
// Browser
8+
// use this instead of window, since window might not exist and throw and error
9+
this.tarball = tarball;
10+
}
11+
312
tarball.TarReader = class {
413
constructor() {
514
this.fileInfo = [];
@@ -19,12 +28,10 @@ tarball.TarReader = class {
1928
}
2029

2130
readArrayBuffer(arrayBuffer) {
22-
return new Promise((resolve, reject) => {
23-
this.buffer = arrayBuffer;
24-
this.fileInfo = [];
25-
this._readFileInfo();
26-
resolve(this.fileInfo);
27-
});
31+
this.buffer = arrayBuffer;
32+
this.fileInfo = [];
33+
this._readFileInfo();
34+
return this.fileInfo;
2835
}
2936

3037
_readFileInfo() {
@@ -61,13 +68,9 @@ tarball.TarReader = class {
6168

6269
_readString(str_offset, size) {
6370
let strView = new Uint8Array(this.buffer, str_offset, size);
64-
let i = 0;
65-
let rtnStr = "";
66-
while(strView[i] != 0) {
67-
rtnStr += String.fromCharCode(strView[i]);
68-
i++;
69-
}
70-
return rtnStr;
71+
let i = strView.indexOf(0);
72+
let td = new TextDecoder();
73+
return td.decode(strView.slice(0, i));
7174
}
7275

7376
_readFileName(header_offset) {
@@ -104,30 +107,37 @@ tarball.TarReader = class {
104107
return blob;
105108
}
106109

110+
_readFileBinary(file_offset, size) {
111+
let view = new Uint8Array(this.buffer, file_offset, size);
112+
return view;
113+
}
114+
107115
_readTextFile(file_offset, size) {
108116
let view = new Uint8Array(this.buffer, file_offset, size);
109-
let data = "";
110-
for(let i = 0; i < size; i++) {
111-
data += String.fromCharCode(view[i]);
112-
}
113-
return data;
117+
let td = new TextDecoder();
118+
return td.decode(view);
114119
}
115120

116121
getTextFile(file_name) {
117-
let i = this.fileInfo.findIndex(info => info.name == file_name);
118-
if(i >= 0) {
119-
let info = this.fileInfo[i];
122+
let info = this.fileInfo.find(info => info.name == file_name);
123+
if (info) {
120124
return this._readTextFile(info.header_offset+512, info.size);
121125
}
122126
}
123127

124128
getFileBlob(file_name, mimetype) {
125-
let i = this.fileInfo.findIndex(info => info.name == file_name);
126-
if(i >= 0) {
127-
let info = this.fileInfo[i];
129+
let info = this.fileInfo.find(info => info.name == file_name);
130+
if (info) {
128131
return this._readFileBlob(info.header_offset+512, info.size, mimetype);
129132
}
130133
}
134+
135+
getFileBinary(file_name) {
136+
let info = this.fileInfo.find(info => info.name == file_name);
137+
if (info) {
138+
return this._readFileBinary(info.header_offset+512, info.size);
139+
}
140+
}
131141
};
132142

133143
tarball.TarWriter = class {
@@ -136,11 +146,8 @@ tarball.TarWriter = class {
136146
}
137147

138148
addTextFile(name, text, opts) {
139-
let buf = new ArrayBuffer(text.length);
140-
let arr = new Uint8Array(buf);
141-
for(let i = 0; i < text.length; i++) {
142-
arr[i] = text.charCodeAt(i);
143-
}
149+
let te = new TextEncoder();
150+
let arr = te.encode(text);
144151
this.fileData.push({
145152
name: name,
146153
array: arr,
@@ -201,7 +208,7 @@ tarball.TarWriter = class {
201208
}
202209

203210
async download(filename) {
204-
let blob = await this.write();
211+
let blob = await this.writeBlob();
205212
let $downloadElem = document.createElement('a');
206213
$downloadElem.href = URL.createObjectURL(blob);
207214
$downloadElem.download = filename;
@@ -211,17 +218,23 @@ tarball.TarWriter = class {
211218
document.body.removeChild($downloadElem);
212219
}
213220

214-
write() {
221+
async writeBlob(onUpdate) {
222+
return new Blob([await this.write(onUpdate)], {"type":"application/x-tar"});
223+
}
224+
225+
write(onUpdate) {
215226
return new Promise((resolve,reject) => {
216227
this._createBuffer();
217228
let offset = 0;
218229
let filesAdded = 0;
219230
let onFileDataAdded = () => {
220231
filesAdded++;
232+
if (onUpdate) {
233+
onUpdate(filesAdded / this.fileData.length * 100);
234+
}
221235
if(filesAdded === this.fileData.length) {
222236
let arr = new Uint8Array(this.buffer);
223-
let blob = new Blob([arr], {"type":"application/x-tar"});
224-
resolve(blob);
237+
resolve(arr);
225238
}
226239
};
227240
for(let fileIdx = 0; fileIdx < this.fileData.length; fileIdx++) {
@@ -269,12 +282,19 @@ tarball.TarWriter = class {
269282

270283
_writeString(str, offset, size) {
271284
let strView = new Uint8Array(this.buffer, offset, size);
272-
for(let i = 0; i < size; i++) {
273-
if(i < str.length) {
274-
strView[i] = str.charCodeAt(i);
275-
} else {
285+
let te = new TextEncoder();
286+
if (te.encodeInto) {
287+
// let the browser write directly into the buffer
288+
let written = te.encodeInto(str, strView).written;
289+
for (let i = written; i < size; i++) {
276290
strView[i] = 0;
277291
}
292+
} else {
293+
// browser can't write directly into the buffer, do it manually
294+
let arr = te.encode(str);
295+
for (let i = 0; i < size; i++) {
296+
strView[i] = i < arr.length ? arr[i] : 0;
297+
}
278298
}
279299
}
280300

tests/files/simple.tar

0 Bytes
Binary file not shown.

tests/files/simple/hello.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
hello world!
1+
hello world! 🙂

tests/tests.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ let testUtils = {
1414
return new Promise((resolve, reject) => {
1515
let tarWriter = new tarball.TarWriter();
1616
tarWriter.addFolder("myfolder/");
17-
tarWriter.addTextFile("myfolder/first.txt", "this is some text");
18-
tarWriter.addTextFile("myfolder/second.txt", "some more text");
17+
tarWriter.addTextFile("myfolder/first.txt", "this is some text 🙂");
18+
tarWriter.addTextFile("myfolder/second.txt", "some more text with 🙃 emojis");
1919
fetch("files/simple/tux.png").then(resp => resp.blob()).then((blob) => {
2020
let file = blob;
2121
file.name = "tux.png";
@@ -26,7 +26,7 @@ let testUtils = {
2626
resolve(null);
2727
});
2828
} else {
29-
tarWriter.write().then((tarBlob) => {
29+
tarWriter.writeBlob().then((tarBlob) => {
3030
let tarFile = tarBlob;
3131
let tarReader = new tarball.TarReader();
3232
tarReader.readFile(tarFile).then((fileInfo) => {
@@ -47,8 +47,8 @@ QUnit.test( "Count files", function( assert ) {
4747
let fileInfo = tar.getFileInfo();
4848
assert.equal(fileInfo.length, 3, "Has 3 files");
4949
assert.equal(fileInfo[0].name, "simple/", "Has simple directory");
50-
assert.equal(fileInfo[1].name, "simple/tux.png", "Has image file");
51-
assert.equal(fileInfo[2].name, "simple/hello.txt", "Has text file");
50+
assert.equal(fileInfo[1].name, "simple/hello.txt", "Has text file");
51+
assert.equal(fileInfo[2].name, "simple/tux.png", "Has image file");
5252
done();
5353
});
5454
});
@@ -57,9 +57,9 @@ QUnit.test( "Check file headers", function( assert ) {
5757
let done = assert.async();
5858
testUtils.fetchTar("files/simple.tar").then((tar) => {
5959
let fileInfo = tar.getFileInfo();
60-
assert.equal(fileInfo[1].name, "simple/tux.png", "File name is ok");
61-
assert.equal(fileInfo[1].type, "file", "File type is ok");
62-
assert.equal(fileInfo[1].size, 11913, "File size is ok");
60+
assert.equal(fileInfo[2].name, "simple/tux.png", "File name is ok");
61+
assert.equal(fileInfo[2].type, "file", "File type is ok");
62+
assert.equal(fileInfo[2].size, 11913, "File size is ok");
6363
done();
6464
});
6565
});
@@ -68,7 +68,7 @@ QUnit.test( "Check text file contents", function( assert ) {
6868
let done = assert.async();
6969
testUtils.fetchTar("files/simple.tar").then((tar) => {
7070
let text = tar.getTextFile("simple/hello.txt");
71-
assert.equal(text, "hello world!\n", "Text file contents are ok");
71+
assert.equal(text, "hello world! 🙂\n", "Text file contents are ok");
7272
done();
7373
});
7474
});
@@ -114,7 +114,7 @@ QUnit.test( "Check text file contents", function( assert ) {
114114
let done = assert.async();
115115
testUtils.generateTar().then((tar) => {
116116
let text = tar.getTextFile("myfolder/second.txt");
117-
assert.equal(text, "some more text", "text file contents are ok");
117+
assert.equal(text, "some more text with 🙃 emojis", "text file contents are ok");
118118
done();
119119
});
120120
});

0 commit comments

Comments
 (0)