Example works
This commit is contained in:
commit
690bca781f
7 changed files with 458 additions and 0 deletions
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2011 T. Jameson Little
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
45
README.md
Normal file
45
README.md
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
Intro
|
||||||
|
=====
|
||||||
|
|
||||||
|
Have you ever wanted to tar something, but you didn't want to push it to your server first?
|
||||||
|
|
||||||
|
Tar-js is here to the rescue!!
|
||||||
|
|
||||||
|
With tar-js, you can construct a tar archive in the browser. This is basically a port of tar-async for Nodejs for the browser, with a couple differences.
|
||||||
|
|
||||||
|
Here's what it supports:
|
||||||
|
* Add strings to a tar archive as files
|
||||||
|
* Customizable uid, gid, mtime, and permissions (defaults work well though too)
|
||||||
|
* Add files in a directory heirarchy
|
||||||
|
|
||||||
|
Dependencies
|
||||||
|
------------
|
||||||
|
|
||||||
|
Tar needs an HTML5 compliant browser. More specifically it needs `Uint8Array` to work.
|
||||||
|
|
||||||
|
The only external module is require-kiss, which makes browser JS much more Node-like.
|
||||||
|
|
||||||
|
This module can be installed from npm (`npm install require-kiss`) or directly downloaded from github (https://github.com/coolaj86/require-kiss-js).
|
||||||
|
|
||||||
|
Usage Guide
|
||||||
|
===========
|
||||||
|
|
||||||
|
In your HTML file, make sure that require-kiss is included first. Then, to use it, do something like this:
|
||||||
|
|
||||||
|
var Tar = require('tar-js'),
|
||||||
|
tape = new Tar();
|
||||||
|
|
||||||
|
Then all you got to do is call `tape.append` with your params and it'll be added to the archive. That's it!
|
||||||
|
|
||||||
|
Here's the api for append: `append(filepath, content, [opts], [callback])`
|
||||||
|
|
||||||
|
* filepath- string path (can include directories and such)
|
||||||
|
* content- string or Uint8Array
|
||||||
|
* opts- options:
|
||||||
|
* mode- permissions of resulting file (octet) [default: 777]
|
||||||
|
* mtime- modification time in seconds (integer) [default: current time]
|
||||||
|
* uid- user id (integer) [default: 0]
|
||||||
|
* gid- group id (integer) [default: 0]
|
||||||
|
* callback- callback when done (takes a Uint8Array as it's only parameter)
|
||||||
|
* This is a reference to the tar so far
|
||||||
|
* Copy it if you want to use it, because subsequent adds may break stuff
|
36
examples/index.html
Normal file
36
examples/index.html
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>Browser Tar</title>
|
||||||
|
|
||||||
|
<script src='../node_modules/require-kiss/require-kiss.js'></script>
|
||||||
|
<script src='../lib/utils.js'></script>
|
||||||
|
<script src='../lib/header.js'></script>
|
||||||
|
<script src='../lib/tar.js'></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var Tar = require('tar'),
|
||||||
|
utils = require('utils'),
|
||||||
|
tape = new Tar(),
|
||||||
|
out,
|
||||||
|
url,
|
||||||
|
base64;
|
||||||
|
|
||||||
|
out = tape.append('output.txt', 'This is test1!');
|
||||||
|
out = tape.append('dir/out.txt', 'This is test2! I changed up the directory');
|
||||||
|
out = tape.append('arr.txt', utils.stringToUint8('This is a Uint8Array!'));
|
||||||
|
|
||||||
|
base64 = utils.uint8ToBase64(out);
|
||||||
|
|
||||||
|
url = "data:application/tar;base64," + base64;
|
||||||
|
window.open(url);
|
||||||
|
}());
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
</body>
|
||||||
|
</html>
|
129
lib/header.js
Normal file
129
lib/header.js
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
/*
|
||||||
|
* tar-js
|
||||||
|
* MIT (c) 2011 T. Jameson Little
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
require('require-kiss');
|
||||||
|
|
||||||
|
/*
|
||||||
|
struct posix_header { // byte offset
|
||||||
|
char name[100]; // 0
|
||||||
|
char mode[8]; // 100
|
||||||
|
char uid[8]; // 108
|
||||||
|
char gid[8]; // 116
|
||||||
|
char size[12]; // 124
|
||||||
|
char mtime[12]; // 136
|
||||||
|
char chksum[8]; // 148
|
||||||
|
char typeflag; // 156
|
||||||
|
char linkname[100]; // 157
|
||||||
|
char magic[6]; // 257
|
||||||
|
char version[2]; // 263
|
||||||
|
char uname[32]; // 265
|
||||||
|
char gname[32]; // 297
|
||||||
|
char devmajor[8]; // 329
|
||||||
|
char devminor[8]; // 337
|
||||||
|
char prefix[155]; // 345
|
||||||
|
// 500
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
var utils = require("./utils"),
|
||||||
|
headerFormat;
|
||||||
|
|
||||||
|
headerFormat = [
|
||||||
|
{
|
||||||
|
'field': 'fileName',
|
||||||
|
'length': 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'field': 'fileMode',
|
||||||
|
'length': 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'field': 'uid',
|
||||||
|
'length': 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'field': 'gid',
|
||||||
|
'length': 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'field': 'fileSize',
|
||||||
|
'length': 12
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'field': 'mtime',
|
||||||
|
'length': 12
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'field': 'checksum',
|
||||||
|
'length': 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'field': 'type',
|
||||||
|
'length': 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'field': 'linkName',
|
||||||
|
'length': 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'field': 'ustar',
|
||||||
|
'length': 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'field': 'owner',
|
||||||
|
'length': 32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'field': 'group',
|
||||||
|
'length': 32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'field': 'majorNumber',
|
||||||
|
'length': 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'field': 'minorNumber',
|
||||||
|
'length': 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'field': 'filenamePrefix',
|
||||||
|
'length': 155
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'field': 'padding',
|
||||||
|
'length': 12
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
function formatHeader(data, cb) {
|
||||||
|
var buffer = utils.clean(512),
|
||||||
|
offset = 0;
|
||||||
|
|
||||||
|
headerFormat.forEach(function (value) {
|
||||||
|
var str = data[value.field] || "",
|
||||||
|
i, length;
|
||||||
|
|
||||||
|
for (i = 0, length = str.length; i < length; i += 1) {
|
||||||
|
buffer[offset] = str.charCodeAt(i);
|
||||||
|
offset += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += value.length - i; // space it out with nulls
|
||||||
|
});
|
||||||
|
|
||||||
|
if (typeof cb === 'function') {
|
||||||
|
return cb(buffer, offset);
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.structure = headerFormat;
|
||||||
|
module.exports.format = formatHeader;
|
||||||
|
|
||||||
|
provide('header', module.exports);
|
||||||
|
}());
|
113
lib/tar.js
Normal file
113
lib/tar.js
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
/*
|
||||||
|
* tar-js
|
||||||
|
* MIT (c) 2011 T. Jameson Little
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
require('require-kiss');
|
||||||
|
|
||||||
|
var header = require("./header"),
|
||||||
|
utils = require("./utils"),
|
||||||
|
recordSize = 512,
|
||||||
|
blockSize;
|
||||||
|
|
||||||
|
function Tar(recordsPerBlock) {
|
||||||
|
this.written = 0;
|
||||||
|
blockSize = (recordsPerBlock || 20) * recordSize;
|
||||||
|
this.out = utils.clean(blockSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
Tar.prototype.append = function (filepath, input, opts, callback) {
|
||||||
|
var data,
|
||||||
|
checksum,
|
||||||
|
mode,
|
||||||
|
mtime,
|
||||||
|
uid,
|
||||||
|
gid,
|
||||||
|
headerArr;
|
||||||
|
|
||||||
|
if (typeof input === 'string') {
|
||||||
|
input = utils.stringToUint8(input);
|
||||||
|
} else if (input.constructor !== Uint8Array.prototype.constructor) {
|
||||||
|
throw 'Invalid input type. You gave me: ' + input.constructor.toString().match(/function\s*([$A-Za-z_][0-9A-Za-z_]*)\s*\(/)[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof opts === 'function') {
|
||||||
|
callback = opts;
|
||||||
|
opts = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
opts = opts || {};
|
||||||
|
|
||||||
|
mode = opts.mode || parseInt('777', 8) & 0xfff;
|
||||||
|
mtime = opts.mtime || Math.floor(+new Date() / 1000);
|
||||||
|
uid = opts.uid || 0;
|
||||||
|
gid = opts.gid || 0;
|
||||||
|
|
||||||
|
data = {
|
||||||
|
fileName: filepath,
|
||||||
|
fileMode: utils.pad(mode, 7),
|
||||||
|
uid: utils.pad(uid, 7),
|
||||||
|
gid: utils.pad(gid, 7),
|
||||||
|
fileSize: utils.pad(input.length, 11),
|
||||||
|
mtime: utils.pad(mtime, 11),
|
||||||
|
checksum: ' ',
|
||||||
|
type: '0', // just a file
|
||||||
|
ustar: 'ustar ',
|
||||||
|
owner: '',
|
||||||
|
group: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
// calculate the checksum
|
||||||
|
checksum = 0;
|
||||||
|
Object.keys(data).forEach(function (key) {
|
||||||
|
var i, value = data[key], length;
|
||||||
|
|
||||||
|
for (i = 0, length = value.length; i < length; i += 1) {
|
||||||
|
checksum += value.charCodeAt(i);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
data.checksum = utils.pad(checksum, 6) + "\u0000 ";
|
||||||
|
|
||||||
|
headerArr = header.format(data);
|
||||||
|
|
||||||
|
var i, offset, length;
|
||||||
|
|
||||||
|
this.out.set(headerArr, this.written);
|
||||||
|
|
||||||
|
this.written += headerArr.length;
|
||||||
|
|
||||||
|
// this makes sense if the input is greater than 512 bytes
|
||||||
|
if (headerArr.length + input.length > this.out.length) {
|
||||||
|
this.out = utils.extend(out, headerArr.length, input.length, blockSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.out.set(input, this.written);
|
||||||
|
|
||||||
|
// to the nearest multiple of recordSize
|
||||||
|
this.written += input.length + (recordSize - (input.length % recordSize));
|
||||||
|
|
||||||
|
// make sure there's at least 2 empty records worth of extra space
|
||||||
|
if (this.out.length - this.written < recordSize * 2) {
|
||||||
|
this.out = utils.extend(out, this.written, recordSize * 2, blockSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof callback === 'function') {
|
||||||
|
callback(this.out);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.out;
|
||||||
|
};
|
||||||
|
|
||||||
|
Tar.prototype.clear = function () {
|
||||||
|
this.written = 0;
|
||||||
|
this.out = utils.clean(blockSize);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = Tar;
|
||||||
|
|
||||||
|
provide('tar', module.exports);
|
||||||
|
}());
|
95
lib/utils.js
Normal file
95
lib/utils.js
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
* tar-js
|
||||||
|
* MIT (c) 2011 T. Jameson Little
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
require('require-kiss');
|
||||||
|
|
||||||
|
var lookup = [
|
||||||
|
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||||
|
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||||
|
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||||
|
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||||
|
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
||||||
|
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
|
||||||
|
'w', 'x', 'y', 'z', '0', '1', '2', '3',
|
||||||
|
'4', '5', '6', '7', '8', '9', '+', '/'
|
||||||
|
];
|
||||||
|
function clean(length) {
|
||||||
|
var i, buffer = new Uint8Array(length);
|
||||||
|
for (i = 0; i < length; i += 1) {
|
||||||
|
buffer[i] = 0;
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
function extend(orig, length, addLength, multipleOf) {
|
||||||
|
var newSize = length + addLength,
|
||||||
|
buffer = clean((parseInt(newSize / multipleOf) + 1) * multipleOf);
|
||||||
|
|
||||||
|
buffer.set(orig);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
function pad(num, bytes, base) {
|
||||||
|
num = num.toString(base || 8);
|
||||||
|
return "000000000000".substr(num.length + 12 - bytes) + num;
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringToUint8 (input, out, offset) {
|
||||||
|
var i, length;
|
||||||
|
|
||||||
|
out = out || clean(input.length);
|
||||||
|
|
||||||
|
offset = offset || 0;
|
||||||
|
for (i = 0, length = input.length; i < length; i += 1) {
|
||||||
|
out[offset] = input.charCodeAt(i);
|
||||||
|
offset += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
function uint8ToBase64(uint8) {
|
||||||
|
var i,
|
||||||
|
extraBytes = uint8.length % 3, // if we have 1 byte left, pad 2 bytes
|
||||||
|
output = "",
|
||||||
|
temp, length;
|
||||||
|
|
||||||
|
function tripletToBase64 (num) {
|
||||||
|
return lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F];
|
||||||
|
};
|
||||||
|
|
||||||
|
// go through the array every three bytes, we'll deal with trailing stuff later
|
||||||
|
for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) {
|
||||||
|
temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2]);
|
||||||
|
output += tripletToBase64(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// this prevents an ERR_INVALID_URL in Chrome (Firefox okay)
|
||||||
|
switch (output.length % 4) {
|
||||||
|
case 1:
|
||||||
|
output += '=';
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
output += '==';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.clean = clean;
|
||||||
|
module.exports.pad = pad;
|
||||||
|
module.exports.extend = extend;
|
||||||
|
module.exports.stringToUint8 = stringToUint8;
|
||||||
|
module.exports.uint8ToBase64 = uint8ToBase64;
|
||||||
|
|
||||||
|
provide('utils');
|
||||||
|
}());
|
19
package.json
Normal file
19
package.json
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"name": "tar-js",
|
||||||
|
"description": "Tar implemented in the browser",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"homepage": "http://github.com/beatgammit/tar-js",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git://github.com/beatgammit/tar-js.git"
|
||||||
|
},
|
||||||
|
"author": "T. Jameson Little <t.jameson.little@gmail.com>",
|
||||||
|
"main": "lib/tar.js",
|
||||||
|
"keywords": ["tar", "browser", "client", "offline"],
|
||||||
|
"directories": {
|
||||||
|
"lib": "lib"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"require-kiss": "*"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue