001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019 package org.apache.commons.compress.archivers; 020 021 import java.io.ByteArrayInputStream; 022 import java.io.IOException; 023 import java.io.InputStream; 024 import java.io.OutputStream; 025 026 import org.apache.commons.compress.archivers.ar.ArArchiveInputStream; 027 import org.apache.commons.compress.archivers.ar.ArArchiveOutputStream; 028 import org.apache.commons.compress.archivers.cpio.CpioArchiveInputStream; 029 import org.apache.commons.compress.archivers.cpio.CpioArchiveOutputStream; 030 import org.apache.commons.compress.archivers.dump.DumpArchiveInputStream; 031 import org.apache.commons.compress.archivers.jar.JarArchiveInputStream; 032 import org.apache.commons.compress.archivers.jar.JarArchiveOutputStream; 033 import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; 034 import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; 035 import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; 036 import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; 037 038 /** 039 * <p>Factory to create Archive[In|Out]putStreams from names or the first bytes of 040 * the InputStream. In order add other implementations you should extend 041 * ArchiveStreamFactory and override the appropriate methods (and call their 042 * implementation from super of course).</p> 043 * 044 * Compressing a ZIP-File: 045 * 046 * <pre> 047 * final OutputStream out = new FileOutputStream(output); 048 * ArchiveOutputStream os = new ArchiveStreamFactory().createArchiveOutputStream(ArchiveStreamFactory.ZIP, out); 049 * 050 * os.putArchiveEntry(new ZipArchiveEntry("testdata/test1.xml")); 051 * IOUtils.copy(new FileInputStream(file1), os); 052 * os.closeArchiveEntry(); 053 * 054 * os.putArchiveEntry(new ZipArchiveEntry("testdata/test2.xml")); 055 * IOUtils.copy(new FileInputStream(file2), os); 056 * os.closeArchiveEntry(); 057 * os.close(); 058 * </pre> 059 * 060 * Decompressing a ZIP-File: 061 * 062 * <pre> 063 * final InputStream is = new FileInputStream(input); 064 * ArchiveInputStream in = new ArchiveStreamFactory().createArchiveInputStream(ArchiveStreamFactory.ZIP, is); 065 * ZipArchiveEntry entry = (ZipArchiveEntry)in.getNextEntry(); 066 * OutputStream out = new FileOutputStream(new File(dir, entry.getName())); 067 * IOUtils.copy(in, out); 068 * out.close(); 069 * in.close(); 070 * </pre> 071 * 072 * @Immutable 073 */ 074 public class ArchiveStreamFactory { 075 076 /** 077 * Constant used to identify the AR archive format. 078 * @since Commons Compress 1.1 079 */ 080 public static final String AR = "ar"; 081 /** 082 * Constant used to identify the CPIO archive format. 083 * @since Commons Compress 1.1 084 */ 085 public static final String CPIO = "cpio"; 086 /** 087 * Constant used to identify the Unix DUMP archive format. 088 * @since Commons Compress 1.3 089 */ 090 public static final String DUMP = "dump"; 091 /** 092 * Constant used to identify the JAR archive format. 093 * @since Commons Compress 1.1 094 */ 095 public static final String JAR = "jar"; 096 /** 097 * Constant used to identify the TAR archive format. 098 * @since Commons Compress 1.1 099 */ 100 public static final String TAR = "tar"; 101 /** 102 * Constant used to identify the ZIP archive format. 103 * @since Commons Compress 1.1 104 */ 105 public static final String ZIP = "zip"; 106 107 /** 108 * Create an archive input stream from an archiver name and an input stream. 109 * 110 * @param archiverName the archive name, i.e. "ar", "zip", "tar", "jar", "dump" or "cpio" 111 * @param in the input stream 112 * @return the archive input stream 113 * @throws ArchiveException if the archiver name is not known 114 * @throws IllegalArgumentException if the archiver name or stream is null 115 */ 116 public ArchiveInputStream createArchiveInputStream( 117 final String archiverName, final InputStream in) 118 throws ArchiveException { 119 120 if (archiverName == null) { 121 throw new IllegalArgumentException("Archivername must not be null."); 122 } 123 124 if (in == null) { 125 throw new IllegalArgumentException("InputStream must not be null."); 126 } 127 128 if (AR.equalsIgnoreCase(archiverName)) { 129 return new ArArchiveInputStream(in); 130 } 131 if (ZIP.equalsIgnoreCase(archiverName)) { 132 return new ZipArchiveInputStream(in); 133 } 134 if (TAR.equalsIgnoreCase(archiverName)) { 135 return new TarArchiveInputStream(in); 136 } 137 if (JAR.equalsIgnoreCase(archiverName)) { 138 return new JarArchiveInputStream(in); 139 } 140 if (CPIO.equalsIgnoreCase(archiverName)) { 141 return new CpioArchiveInputStream(in); 142 } 143 if (DUMP.equalsIgnoreCase(archiverName)) { 144 return new DumpArchiveInputStream(in); 145 } 146 147 throw new ArchiveException("Archiver: " + archiverName + " not found."); 148 } 149 150 /** 151 * Create an archive output stream from an archiver name and an input stream. 152 * 153 * @param archiverName the archive name, i.e. "ar", "zip", "tar", "jar" or "cpio" 154 * @param out the output stream 155 * @return the archive output stream 156 * @throws ArchiveException if the archiver name is not known 157 * @throws IllegalArgumentException if the archiver name or stream is null 158 */ 159 public ArchiveOutputStream createArchiveOutputStream( 160 final String archiverName, final OutputStream out) 161 throws ArchiveException { 162 if (archiverName == null) { 163 throw new IllegalArgumentException("Archivername must not be null."); 164 } 165 if (out == null) { 166 throw new IllegalArgumentException("OutputStream must not be null."); 167 } 168 169 if (AR.equalsIgnoreCase(archiverName)) { 170 return new ArArchiveOutputStream(out); 171 } 172 if (ZIP.equalsIgnoreCase(archiverName)) { 173 return new ZipArchiveOutputStream(out); 174 } 175 if (TAR.equalsIgnoreCase(archiverName)) { 176 return new TarArchiveOutputStream(out); 177 } 178 if (JAR.equalsIgnoreCase(archiverName)) { 179 return new JarArchiveOutputStream(out); 180 } 181 if (CPIO.equalsIgnoreCase(archiverName)) { 182 return new CpioArchiveOutputStream(out); 183 } 184 throw new ArchiveException("Archiver: " + archiverName + " not found."); 185 } 186 187 /** 188 * Create an archive input stream from an input stream, autodetecting 189 * the archive type from the first few bytes of the stream. The InputStream 190 * must support marks, like BufferedInputStream. 191 * 192 * @param in the input stream 193 * @return the archive input stream 194 * @throws ArchiveException if the archiver name is not known 195 * @throws IllegalArgumentException if the stream is null or does not support mark 196 */ 197 public ArchiveInputStream createArchiveInputStream(final InputStream in) 198 throws ArchiveException { 199 if (in == null) { 200 throw new IllegalArgumentException("Stream must not be null."); 201 } 202 203 if (!in.markSupported()) { 204 throw new IllegalArgumentException("Mark is not supported."); 205 } 206 207 final byte[] signature = new byte[12]; 208 in.mark(signature.length); 209 try { 210 int signatureLength = in.read(signature); 211 in.reset(); 212 if (ZipArchiveInputStream.matches(signature, signatureLength)) { 213 return new ZipArchiveInputStream(in); 214 } else if (JarArchiveInputStream.matches(signature, signatureLength)) { 215 return new JarArchiveInputStream(in); 216 } else if (ArArchiveInputStream.matches(signature, signatureLength)) { 217 return new ArArchiveInputStream(in); 218 } else if (CpioArchiveInputStream.matches(signature, signatureLength)) { 219 return new CpioArchiveInputStream(in); 220 } 221 222 // Dump needs a bigger buffer to check the signature; 223 final byte[] dumpsig = new byte[32]; 224 in.mark(dumpsig.length); 225 signatureLength = in.read(dumpsig); 226 in.reset(); 227 if (DumpArchiveInputStream.matches(dumpsig, signatureLength)) { 228 return new DumpArchiveInputStream(in); 229 } 230 231 // Tar needs an even bigger buffer to check the signature; read the first block 232 final byte[] tarheader = new byte[512]; 233 in.mark(tarheader.length); 234 signatureLength = in.read(tarheader); 235 in.reset(); 236 if (TarArchiveInputStream.matches(tarheader, signatureLength)) { 237 return new TarArchiveInputStream(in); 238 } 239 // COMPRESS-117 - improve auto-recognition 240 if (signatureLength >= 512) { 241 try { 242 TarArchiveInputStream tais = new TarArchiveInputStream(new ByteArrayInputStream(tarheader)); 243 tais.getNextEntry(); 244 return new TarArchiveInputStream(in); 245 } catch (Exception e) { // NOPMD 246 // can generate IllegalArgumentException as well 247 // as IOException 248 // autodetection, simply not a TAR 249 // ignored 250 } 251 } 252 } catch (IOException e) { 253 throw new ArchiveException("Could not use reset and mark operations.", e); 254 } 255 256 throw new ArchiveException("No Archiver found for the stream signature"); 257 } 258 }