]> WPIA git - gigi.git/blob - lib/jtar/org/kamranzafar/jtar/TarOutputStream.java
Adding jtar
[gigi.git] / lib / jtar / org / kamranzafar / jtar / TarOutputStream.java
1 /**
2  * Copyright 2012 Kamran Zafar 
3  * 
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 
7  * 
8  *      http://www.apache.org/licenses/LICENSE-2.0 
9  * 
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. 
15  * 
16  */
17
18 package org.kamranzafar.jtar;
19
20 import java.io.BufferedOutputStream;
21 import java.io.File;
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;
27
28 /**
29  * @author Kamran Zafar
30  * 
31  */
32 public class TarOutputStream extends OutputStream {
33         private final OutputStream out;
34     private long bytesWritten;
35     private long currentFileSize;
36     private TarEntry currentEntry;
37
38     public TarOutputStream(OutputStream out) {
39         this.out = out;
40         bytesWritten = 0;
41         currentFileSize = 0;
42     }
43
44         public TarOutputStream(final File fout) throws FileNotFoundException {
45                 this.out = new BufferedOutputStream(new FileOutputStream(fout));
46                 bytesWritten = 0;
47                 currentFileSize = 0;
48         }
49
50         /**
51          * Opens a file for writing. 
52          */
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);
59                 }
60                 out = new BufferedOutputStream(new FileOutputStream(raf.getFD()));
61         }
62
63     /**
64      * Appends the EOF record and closes the stream
65      * 
66      * @see java.io.FilterOutputStream#close()
67      */
68     @Override
69     public void close() throws IOException {
70         closeCurrentEntry();
71         write( new byte[TarConstants.EOF_BLOCK] );
72         out.close();
73     }
74     /**
75      * Writes a byte to the stream and updates byte counters
76      * 
77      * @see java.io.FilterOutputStream#write(int)
78      */
79     @Override
80     public void write(int b) throws IOException {
81         out.write( b );
82         bytesWritten += 1;
83
84         if (currentEntry != null) {
85             currentFileSize += 1;
86         }
87     }
88
89     /**
90      * Checks if the bytes being written exceed the current entry size.
91      * 
92      * @see java.io.FilterOutputStream#write(byte[], int, int)
93      */
94     @Override
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." );
101             }
102         }
103
104         out.write( b, off, len );
105         
106         bytesWritten += len;
107
108         if (currentEntry != null) {
109             currentFileSize += len;
110         }        
111     }
112
113     /**
114      * Writes the next tar entry header on the stream
115      * 
116      * @param entry
117      * @throws IOException
118      */
119     public void putNextEntry(TarEntry entry) throws IOException {
120         closeCurrentEntry();
121
122         byte[] header = new byte[TarConstants.HEADER_BLOCK];
123         entry.writeEntryHeader( header );
124
125         write( header );
126
127         currentEntry = entry;
128     }
129
130     /**
131      * Closes the current tar entry
132      * 
133      * @throws IOException
134      */
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." );
140             }
141
142             currentEntry = null;
143             currentFileSize = 0;
144
145             pad();
146         }
147     }
148
149     /**
150      * Pads the last content block
151      * 
152      * @throws IOException
153      */
154     protected void pad() throws IOException {
155         if (bytesWritten > 0) {
156             int extra = (int) ( bytesWritten % TarConstants.DATA_BLOCK );
157
158             if (extra > 0) {
159                 write( new byte[TarConstants.DATA_BLOCK - extra] );
160             }
161         }
162     }
163 }