2 * Copyright 2012 Kamran Zafar
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 package org.kamranzafar.jtar;
20 import java.io.BufferedOutputStream;
22 import java.io.FileNotFoundException;
23 import java.io.FileOutputStream;
24 import java.io.IOException;
25 import java.io.OutputStream;
26 import java.io.RandomAccessFile;
29 * @author Kamran Zafar
32 public class TarOutputStream extends OutputStream {
33 private final OutputStream out;
34 private long bytesWritten;
35 private long currentFileSize;
36 private TarEntry currentEntry;
38 public TarOutputStream(OutputStream out) {
44 public TarOutputStream(final File fout) throws FileNotFoundException {
45 this.out = new BufferedOutputStream(new FileOutputStream(fout));
51 * Opens a file for writing.
53 public TarOutputStream(final File fout, final boolean append) throws IOException {
54 @SuppressWarnings("resource")
55 RandomAccessFile raf = new RandomAccessFile(fout, "rw");
56 final long fileSize = fout.length();
57 if (append && fileSize > TarConstants.EOF_BLOCK) {
58 raf.seek(fileSize - TarConstants.EOF_BLOCK);
60 out = new BufferedOutputStream(new FileOutputStream(raf.getFD()));
64 * Appends the EOF record and closes the stream
66 * @see java.io.FilterOutputStream#close()
69 public void close() throws IOException {
71 write( new byte[TarConstants.EOF_BLOCK] );
75 * Writes a byte to the stream and updates byte counters
77 * @see java.io.FilterOutputStream#write(int)
80 public void write(int b) throws IOException {
84 if (currentEntry != null) {
90 * Checks if the bytes being written exceed the current entry size.
92 * @see java.io.FilterOutputStream#write(byte[], int, int)
95 public void write(byte[] b, int off, int len) throws IOException {
96 if (currentEntry != null && !currentEntry.isDirectory()) {
97 if (currentEntry.getSize() < currentFileSize + len) {
98 throw new IOException( "The current entry[" + currentEntry.getName() + "] size["
99 + currentEntry.getSize() + "] is smaller than the bytes[" + ( currentFileSize + len )
100 + "] being written." );
104 out.write( b, off, len );
108 if (currentEntry != null) {
109 currentFileSize += len;
114 * Writes the next tar entry header on the stream
117 * @throws IOException
119 public void putNextEntry(TarEntry entry) throws IOException {
122 byte[] header = new byte[TarConstants.HEADER_BLOCK];
123 entry.writeEntryHeader( header );
127 currentEntry = entry;
131 * Closes the current tar entry
133 * @throws IOException
135 protected void closeCurrentEntry() throws IOException {
136 if (currentEntry != null) {
137 if (currentEntry.getSize() > currentFileSize) {
138 throw new IOException( "The current entry[" + currentEntry.getName() + "] of size["
139 + currentEntry.getSize() + "] has not been fully written." );
150 * Pads the last content block
152 * @throws IOException
154 protected void pad() throws IOException {
155 if (bytesWritten > 0) {
156 int extra = (int) ( bytesWritten % TarConstants.DATA_BLOCK );
159 write( new byte[TarConstants.DATA_BLOCK - extra] );