Example works
This commit is contained in:
commit
690bca781f
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…
Reference in a new issue