Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Example works

  • Loading branch information...
commit 690bca781fa5f5444603fd24dc34e7042738d03c 0 parents
@beatgammit authored
21 LICENSE
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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": "*"
+ }
+}
Please sign in to comment.
Something went wrong with that request. Please try again.