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.jar.JarArchiveInputStream; 031 import org.apache.commons.compress.archivers.jar.JarArchiveOutputStream; 032 import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; 033 import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; 034 import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; 035 import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; 036 037 /** 038 * <p>Factory to create Archive[In|Out]putStreams from names or the first bytes of 039 * the InputStream. In order add other implementations you should extend 040 * ArchiveStreamFactory and override the appropriate methods (and call their 041 * implementation from super of course).</p> 042 * 043 * Compressing a ZIP-File: 044 * 045 * <pre> 046 * final OutputStream out = new FileOutputStream(output); 047 * ArchiveOutputStream os = new ArchiveStreamFactory().createArchiveOutputStream(ArchiveStreamFactory.ZIP, out); 048 * 049 * os.putArchiveEntry(new ZipArchiveEntry("testdata/test1.xml")); 050 * IOUtils.copy(new FileInputStream(file1), os); 051 * os.closeArchiveEntry(); 052 * 053 * os.putArchiveEntry(new ZipArchiveEntry("testdata/test2.xml")); 054 * IOUtils.copy(new FileInputStream(file2), os); 055 * os.closeArchiveEntry(); 056 * os.close(); 057 * </pre> 058 * 059 * Decompressing a ZIP-File: 060 * 061 * <pre> 062 * final InputStream is = new FileInputStream(input); 063 * ArchiveInputStream in = new ArchiveStreamFactory().createArchiveInputStream(ArchiveStreamFactory.ZIP, is); 064 * ZipArchiveEntry entry = (ZipArchiveEntry)in.getNextEntry(); 065 * OutputStream out = new FileOutputStream(new File(dir, entry.getName())); 066 * IOUtils.copy(in, out); 067 * out.close(); 068 * in.close(); 069 * </pre> 070 * 071 * @Immutable 072 */ 073 public class ArchiveStreamFactory { 074 075 /** 076 * Constant used to identify the AR archive format. 077 * @since Commons Compress 1.1 078 */ 079 public static final String AR = "ar"; 080 /** 081 * Constant used to identify the CPIO archive format. 082 * @since Commons Compress 1.1 083 */ 084 public static final String CPIO = "cpio"; 085 /** 086 * Constant used to identify the JAR archive format. 087 * @since Commons Compress 1.1 088 */ 089 public static final String JAR = "jar"; 090 /** 091 * Constant used to identify the TAR archive format. 092 * @since Commons Compress 1.1 093 */ 094 public static final String TAR = "tar"; 095 /** 096 * Constant used to identify the ZIP archive format. 097 * @since Commons Compress 1.1 098 */ 099 public static final String ZIP = "zip"; 100 101 /** 102 * Create an archive input stream from an archiver name and an input stream. 103 * 104 * @param archiverName the archive name, i.e. "ar", "zip", "tar", "jar" or "cpio" 105 * @param in the input stream 106 * @return the archive input stream 107 * @throws ArchiveException if the archiver name is not known 108 * @throws IllegalArgumentException if the archiver name or stream is null 109 */ 110 public ArchiveInputStream createArchiveInputStream( 111 final String archiverName, final InputStream in) 112 throws ArchiveException { 113 114 if (archiverName == null) { 115 throw new IllegalArgumentException("Archivername must not be null."); 116 } 117 118 if (in == null) { 119 throw new IllegalArgumentException("InputStream must not be null."); 120 } 121 122 if (AR.equalsIgnoreCase(archiverName)) { 123 return new ArArchiveInputStream(in); 124 } 125 if (ZIP.equalsIgnoreCase(archiverName)) { 126 return new ZipArchiveInputStream(in); 127 } 128 if (TAR.equalsIgnoreCase(archiverName)) { 129 return new TarArchiveInputStream(in); 130 } 131 if (JAR.equalsIgnoreCase(archiverName)) { 132 return new JarArchiveInputStream(in); 133 } 134 if (CPIO.equalsIgnoreCase(archiverName)) { 135 return new CpioArchiveInputStream(in); 136 } 137 138 throw new ArchiveException("Archiver: " + archiverName + " not found."); 139 } 140 141 /** 142 * Create an archive output stream from an archiver name and an input stream. 143 * 144 * @param archiverName the archive name, i.e. "ar", "zip", "tar", "jar" or "cpio" 145 * @param out the output stream 146 * @return the archive output stream 147 * @throws ArchiveException if the archiver name is not known 148 * @throws IllegalArgumentException if the archiver name or stream is null 149 */ 150 public ArchiveOutputStream createArchiveOutputStream( 151 final String archiverName, final OutputStream out) 152 throws ArchiveException { 153 if (archiverName == null) { 154 throw new IllegalArgumentException("Archivername must not be null."); 155 } 156 if (out == null) { 157 throw new IllegalArgumentException("OutputStream must not be null."); 158 } 159 160 if (AR.equalsIgnoreCase(archiverName)) { 161 return new ArArchiveOutputStream(out); 162 } 163 if (ZIP.equalsIgnoreCase(archiverName)) { 164 return new ZipArchiveOutputStream(out); 165 } 166 if (TAR.equalsIgnoreCase(archiverName)) { 167 return new TarArchiveOutputStream(out); 168 } 169 if (JAR.equalsIgnoreCase(archiverName)) { 170 return new JarArchiveOutputStream(out); 171 } 172 if (CPIO.equalsIgnoreCase(archiverName)) { 173 return new CpioArchiveOutputStream(out); 174 } 175 throw new ArchiveException("Archiver: " + archiverName + " not found."); 176 } 177 178 /** 179 * Create an archive input stream from an input stream, autodetecting 180 * the archive type from the first few bytes of the stream. The InputStream 181 * must support marks, like BufferedInputStream. 182 * 183 * @param in the input stream 184 * @return the archive input stream 185 * @throws ArchiveException if the archiver name is not known 186 * @throws IllegalArgumentException if the stream is null or does not support mark 187 */ 188 public ArchiveInputStream createArchiveInputStream(final InputStream in) 189 throws ArchiveException { 190 if (in == null) { 191 throw new IllegalArgumentException("Stream must not be null."); 192 } 193 194 if (!in.markSupported()) { 195 throw new IllegalArgumentException("Mark is not supported."); 196 } 197 198 final byte[] signature = new byte[12]; 199 in.mark(signature.length); 200 try { 201 int signatureLength = in.read(signature); 202 in.reset(); 203 if (ZipArchiveInputStream.matches(signature, signatureLength)) { 204 return new ZipArchiveInputStream(in); 205 } else if (JarArchiveInputStream.matches(signature, signatureLength)) { 206 return new JarArchiveInputStream(in); 207 } else if (ArArchiveInputStream.matches(signature, signatureLength)) { 208 return new ArArchiveInputStream(in); 209 } else if (CpioArchiveInputStream.matches(signature, signatureLength)) { 210 return new CpioArchiveInputStream(in); 211 } 212 // Tar needs a bigger buffer to check the signature; read the first block 213 final byte[] tarheader = new byte[512]; 214 in.mark(tarheader.length); 215 signatureLength = in.read(tarheader); 216 in.reset(); 217 if (TarArchiveInputStream.matches(tarheader, signatureLength)) { 218 return new TarArchiveInputStream(in); 219 } 220 // COMPRESS-117 - improve auto-recognition 221 try { 222 TarArchiveInputStream tais = new TarArchiveInputStream(new ByteArrayInputStream(tarheader)); 223 tais.getNextEntry(); 224 return new TarArchiveInputStream(in); 225 } catch (Exception e) { // NOPMD 226 // can generate IllegalArgumentException as well as IOException 227 // autodetection, simply not a TAR 228 // ignored 229 } 230 } catch (IOException e) { 231 throw new ArchiveException("Could not use reset and mark operations.", e); 232 } 233 234 throw new ArchiveException("No Archiver found for the stream signature"); 235 } 236 }