commit
690bca781f
@ -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. |
@ -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 |
@ -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> |
@ -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); |
||||
}()); |
@ -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); |
||||
}()); |
@ -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'); |
||||
}()); |
@ -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…
Reference in new issue