Merge pull request #12 from Nimlhug/master

Allow appending to existing tar files.
This commit is contained in:
Kamran 2014-02-12 15:33:38 +00:00
commit e768f32bd1
8 changed files with 297 additions and 30 deletions

View File

@ -53,8 +53,8 @@
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
@ -125,6 +125,6 @@
</profiles>
<properties>
<skipTests>true</skipTests>
<skipTests>false</skipTests>
</properties>
</project>

View File

@ -150,6 +150,14 @@ public class TarInputStream extends FilterInputStream {
return currentEntry;
}
/**
* Returns the current offset (in bytes) from the beginning of the stream.
* This can be used to find out at which point in a tar file an entry's content begins, for instance.
*/
public long getCurrentOffset() {
return bytesRead;
}
/**
* Closes the current tar entry
*

View File

@ -17,25 +17,49 @@
package org.kamranzafar.jtar;
import java.io.FilterOutputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
/**
* @author Kamran Zafar
*
*/
public class TarOutputStream extends FilterOutputStream {
public class TarOutputStream extends OutputStream {
private final OutputStream out;
private long bytesWritten;
private long currentFileSize;
private TarEntry currentEntry;
public TarOutputStream(OutputStream out) {
super( out );
this.out = out;
bytesWritten = 0;
currentFileSize = 0;
}
public TarOutputStream(final File fout) throws FileNotFoundException {
this.out = new BufferedOutputStream(new FileOutputStream(fout));
bytesWritten = 0;
currentFileSize = 0;
}
/**
* Opens a file for writing.
*/
public TarOutputStream(final File fout, final boolean append) throws IOException {
@SuppressWarnings("resource")
RandomAccessFile raf = new RandomAccessFile(fout, "rw");
final long fileSize = fout.length();
if (append && fileSize > TarConstants.EOF_BLOCK) {
raf.seek(fileSize - TarConstants.EOF_BLOCK);
}
out = new BufferedOutputStream(new FileOutputStream(raf.getFD()));
}
/**
* Appends the EOF record and closes the stream
*
@ -45,9 +69,8 @@ public class TarOutputStream extends FilterOutputStream {
public void close() throws IOException {
closeCurrentEntry();
write( new byte[TarConstants.EOF_BLOCK] );
super.close();
out.close();
}
/**
* Writes a byte to the stream and updates byte counters
*
@ -55,7 +78,7 @@ public class TarOutputStream extends FilterOutputStream {
*/
@Override
public void write(int b) throws IOException {
super.write( b );
out.write( b );
bytesWritten += 1;
if (currentEntry != null) {
@ -78,7 +101,13 @@ public class TarOutputStream extends FilterOutputStream {
}
}
super.write( b, off, len );
out.write( b, off, len );
bytesWritten += len;
if (currentEntry != null) {
currentFileSize += len;
}
}
/**

View File

@ -0,0 +1,124 @@
/**
* Copyright 2012 Kamran Zafar
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.kamranzafar.jtar;
import static org.junit.Assert.assertEquals;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.file.Files;
import org.junit.Before;
import org.junit.Test;
public class JTarAppendTest {
static final int BUFFER = 2048;
private File dir;
private File outDir;
private File inDir;
@Before
public void setup() throws IOException {
dir = Files.createTempDirectory("apnd").toFile();
dir.mkdirs();
outDir = new File(dir, "out");
outDir.mkdirs();
inDir = new File(dir, "in");
inDir.mkdirs();
}
@Test
public void testSingleOperation() throws IOException {
TarOutputStream tar = new TarOutputStream(new FileOutputStream(new File(dir, "tar.tar")));
tar.putNextEntry(new TarEntry(TestUtils.writeStringToFile("a", new File(inDir, "afile")), "afile"));
copyFileToStream(new File(inDir, "afile"), tar);
tar.putNextEntry(new TarEntry(TestUtils.writeStringToFile("b", new File(inDir, "bfile")), "bfile"));
copyFileToStream(new File(inDir, "bfile"), tar);
tar.putNextEntry(new TarEntry(TestUtils.writeStringToFile("c", new File(inDir, "cfile")), "cfile"));
copyFileToStream(new File(inDir, "cfile"), tar);
tar.close();
untar();
assertInEqualsOut();
}
@Test
public void testAppend() throws IOException {
TarOutputStream tar = new TarOutputStream(new FileOutputStream(new File(dir, "tar.tar")));
tar.putNextEntry(new TarEntry(TestUtils.writeStringToFile("a", new File(inDir, "afile")), "afile"));
copyFileToStream(new File(inDir, "afile"), tar);
tar.close();
tar = new TarOutputStream(new File(dir, "tar.tar"), true);
tar.putNextEntry(new TarEntry(TestUtils.writeStringToFile("b", new File(inDir, "bfile")), "bfile"));
copyFileToStream(new File(inDir, "bfile"), tar);
tar.putNextEntry(new TarEntry(TestUtils.writeStringToFile("c", new File(inDir, "cfile")), "cfile"));
copyFileToStream(new File(inDir, "cfile"), tar);
tar.close();
untar();
assertInEqualsOut();
}
private void copyFileToStream(File file, OutputStream out) throws IOException {
final byte[] buffer = new byte[BUFFER];
int length = 0;
try (FileInputStream in = new FileInputStream(file)) {
while ((length = in.read(buffer)) > 0) {
out.write(buffer, 0, length);
}
}
}
/**
* Make sure that the contents of the input & output dirs are identical.
*/
private void assertInEqualsOut() throws UnsupportedEncodingException, FileNotFoundException, IOException {
assertEquals(inDir.list().length, outDir.list().length);
for (File in : inDir.listFiles()) {
assertEquals(TestUtils.readFile(in), TestUtils.readFile(new File(outDir, in.getName())));
}
}
private void untar() throws FileNotFoundException, IOException {
try (TarInputStream in = new TarInputStream(new FileInputStream(new File(dir, "tar.tar")))) {
TarEntry entry;
while ((entry = in.getNextEntry()) != null) {
int count;
byte data[] = new byte[2048];
try (BufferedOutputStream dest = new BufferedOutputStream(new FileOutputStream(outDir + "/" + entry.getName()))) {
while ((count = in.read(data)) != -1) {
dest.write(data, 0, count);
}
}
}
}
}
}

View File

@ -17,23 +17,35 @@
package org.kamranzafar.jtar;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.nio.file.Files;
import java.util.zip.GZIPInputStream;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import static org.junit.Assert.*;
@RunWith(JUnit4.class)
public class JTarTest {
static final int BUFFER = 2048;
private File dir;
@Before
public void setup() throws IOException {
dir = Files.createTempDirectory("tartest").toFile();
dir.mkdirs();
}
/**
* Tar the given folder
*
@ -41,15 +53,24 @@ public class JTarTest {
*/
@Test
public void tar() throws IOException {
FileOutputStream dest = new FileOutputStream("/home/kamran/tmp/tartest.tar");
FileOutputStream dest = new FileOutputStream(dir.getAbsolutePath() + "/tartest.tar");
TarOutputStream out = new TarOutputStream(new BufferedOutputStream(dest));
tarFolder(null, "/home/kamran/tmp/tartest/", out);
File tartest = new File(dir.getAbsolutePath(), "tartest");
tartest.mkdirs();
TestUtils.writeStringToFile("HPeX2kD5kSTc7pzCDX", new File(tartest, "one"));
TestUtils.writeStringToFile("gTzyuQjfhrnyX9cTBSy", new File(tartest, "two"));
TestUtils.writeStringToFile("KG889vdgjPHQXUEXCqrr", new File(tartest, "three"));
TestUtils.writeStringToFile("CNBDGjEJNYfms7rwxfkAJ", new File(tartest, "four"));
TestUtils.writeStringToFile("tT6mFKuLRjPmUDjcVTnjBL", new File(tartest, "five"));
TestUtils.writeStringToFile("jrPYpzLfWB5vZTRsSKqFvVj", new File(tartest, "six"));
tarFolder(null, dir.getAbsolutePath() + "/tartest/", out);
out.close();
System.out.println("Calculated tar size: " + TarUtils.calculateTarSize(new File("/home/kamran/tmp/tartest")));
System.out.println("Actual tar size: " + new File("/home/kamran/tmp/tartest.tar").length());
assertEquals(TarUtils.calculateTarSize(new File(dir.getAbsolutePath() + "/tartest")), new File(dir.getAbsolutePath() + "/tartest.tar").length());
}
/**
@ -59,13 +80,17 @@ public class JTarTest {
*/
@Test
public void untarTarFile() throws IOException {
String destFolder = "/home/kamran/tmp/untartest";
File zf = new File("/home/kamran/tmp/tartest.tar");
File destFolder = new File(dir, "untartest");
destFolder.mkdirs();
File zf = new File("src/test/resources/tartest.tar");
TarInputStream tis = new TarInputStream(new BufferedInputStream(new FileInputStream(zf)));
untar(tis, destFolder);
untar(tis, destFolder.getAbsolutePath());
tis.close();
assertFileContents(destFolder);
}
/**
@ -75,15 +100,19 @@ public class JTarTest {
*/
@Test
public void untarTarFileDefaultSkip() throws IOException {
String destFolder = "/home/kamran/tmp/untartest/default";
File zf = new File("/home/kamran/tmp/tartest.tar");
File destFolder = new File(dir, "untartest/skip");
destFolder.mkdirs();
File zf = new File("src/test/resources/tartest.tar");
TarInputStream tis = new TarInputStream(new BufferedInputStream(new FileInputStream(zf)));
tis.setDefaultSkip(true);
untar(tis, destFolder);
untar(tis, destFolder.getAbsolutePath());
tis.close();
assertFileContents(destFolder);
}
/**
@ -93,16 +122,43 @@ public class JTarTest {
*/
@Test
public void untarTGzFile() throws IOException {
String destFolder = "/home/kamran/tmp/untargztest";
File zf = new File("/home/kamran/tmp/tartest.tar.gz");
File destFolder = new File(dir, "untargztest");
File zf = new File("src/test/resources/tartest.tar.gz");
TarInputStream tis = new TarInputStream(new BufferedInputStream(new GZIPInputStream(new FileInputStream(zf))));
untar(tis, destFolder);
untar(tis, destFolder.getAbsolutePath());
tis.close();
assertFileContents(destFolder);
}
@Test
public void testOffset() throws IOException {
File destFolder = new File(dir, "untartest");
destFolder.mkdirs();
File zf = new File("src/test/resources/tartest.tar");
TarInputStream tis = new TarInputStream(new BufferedInputStream(new FileInputStream(zf)));
tis.getNextEntry();
assertEquals(TarConstants.HEADER_BLOCK, tis.getCurrentOffset());
tis.getNextEntry();
TarEntry entry = tis.getNextEntry();
// All of the files in the tartest.tar file are smaller than DATA_BLOCK
assertEquals(TarConstants.HEADER_BLOCK * 3 + TarConstants.DATA_BLOCK * 2, tis.getCurrentOffset());
tis.close();
RandomAccessFile rif = new RandomAccessFile(zf, "r");
rif.seek(TarConstants.HEADER_BLOCK * 3 + TarConstants.DATA_BLOCK * 2);
byte[] data = new byte[(int)entry.getSize()];
rif.read(data);
assertEquals("gTzyuQjfhrnyX9cTBSy", new String(data, "UTF-8"));
rif.close();
}
private void untar(TarInputStream tis, String destFolder) throws IOException {
BufferedOutputStream dest = null;
@ -169,7 +225,6 @@ public class JTarTest {
FileInputStream fi = new FileInputStream(fe);
origin = new BufferedInputStream(fi);
TarEntry entry = new TarEntry(fe, parent + files[i]);
out.putNextEntry(entry);
@ -187,7 +242,6 @@ public class JTarTest {
@Test
public void fileEntry() throws IOException {
String fileName = "file.txt";
long fileSize = 14523;
long modTime = System.currentTimeMillis() / 1000;
@ -209,4 +263,13 @@ public class JTarTest {
TarEntry createdEntry = new TarEntry(headerBuf);
assertTrue(fileEntry.equals(createdEntry));
}
private void assertFileContents(File destFolder) throws UnsupportedEncodingException, FileNotFoundException, IOException {
assertEquals("HPeX2kD5kSTc7pzCDX", TestUtils.readFile(new File(destFolder, "tartest/one")));
assertEquals("gTzyuQjfhrnyX9cTBSy", TestUtils.readFile(new File(destFolder, "tartest/two")));
assertEquals("KG889vdgjPHQXUEXCqrr", TestUtils.readFile(new File(destFolder, "tartest/three")));
assertEquals("CNBDGjEJNYfms7rwxfkAJ", TestUtils.readFile(new File(destFolder, "tartest/four")));
assertEquals("tT6mFKuLRjPmUDjcVTnjBL", TestUtils.readFile(new File(destFolder, "tartest/five")));
assertEquals("jrPYpzLfWB5vZTRsSKqFvVj", TestUtils.readFile(new File(destFolder, "tartest/six")));
}
}

View File

@ -0,0 +1,43 @@
package org.kamranzafar.jtar;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
public class TestUtils {
private static final int BUFFER = 2048;
public static File writeStringToFile(String string, File file) throws UnsupportedEncodingException, FileNotFoundException, IOException {
try (OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(file), "UTF-8")) {
writer.write(string);
}
return file;
}
public static String readFile(File file) throws UnsupportedEncodingException, FileNotFoundException, IOException {
final char[] buffer = new char[BUFFER];
final StringBuilder out = new StringBuilder();
try (final Reader in = new InputStreamReader(new FileInputStream(file), "UTF-8")) {
return readFromStream(buffer, out, in);
}
}
public static String readFromStream(final char[] buffer, final StringBuilder out, final Reader in) throws IOException {
while (true) {
final int read = in.read(buffer, 0, BUFFER);
if (read <= 0) {
break;
}
out.append(buffer, 0, read);
}
return out.toString();
}
}

Binary file not shown.

Binary file not shown.