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.IOException; 022 import java.io.InputStream; 023 import java.io.OutputStream; 024 025 import org.apache.commons.compress.archivers.ar.ArArchiveInputStream; 026 import org.apache.commons.compress.archivers.ar.ArArchiveOutputStream; 027 import org.apache.commons.compress.archivers.cpio.CpioArchiveInputStream; 028 import org.apache.commons.compress.archivers.cpio.CpioArchiveOutputStream; 029 import org.apache.commons.compress.archivers.jar.JarArchiveInputStream; 030 import org.apache.commons.compress.archivers.jar.JarArchiveOutputStream; 031 import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; 032 import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; 033 import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; 034 import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; 035 036 /** 037 * <p>Factory to create Archive[In|Out]putStreams from names or the first bytes of 038 * the InputStream. In order add other implementations you should extend 039 * ArchiveStreamFactory and override the appropriate methods (and call their 040 * implementation from super of course).</p> 041 * 042 * Compressing a ZIP-File: 043 * 044 * <pre> 045 * final OutputStream out = new FileOutputStream(output); 046 * ArchiveOutputStream os = new ArchiveStreamFactory().createArchiveOutputStream("zip", out); 047 * 048 * os.putArchiveEntry(new ZipArchiveEntry("testdata/test1.xml")); 049 * IOUtils.copy(new FileInputStream(file1), os); 050 * os.closeArchiveEntry(); 051 * 052 * os.putArchiveEntry(new ZipArchiveEntry("testdata/test2.xml")); 053 * IOUtils.copy(new FileInputStream(file2), os); 054 * os.closeArchiveEntry(); 055 * os.close(); 056 * </pre> 057 * 058 * Decompressing a ZIP-File: 059 * 060 * <pre> 061 * final InputStream is = new FileInputStream(input); 062 * ArchiveInputStream in = new ArchiveStreamFactory().createArchiveInputStream("zip", is); 063 * ZipArchiveEntry entry = (ZipArchiveEntry)in.getNextEntry(); 064 * OutputStream out = new FileOutputStream(new File(dir, entry.getName())); 065 * IOUtils.copy(in, out); 066 * out.close(); 067 * in.close(); 068 * </pre> 069 * 070 * @Immutable 071 */ 072 public class ArchiveStreamFactory { 073 074 /** 075 * Create an archive input stream from an archiver name and an input stream. 076 * 077 * @param archiverName the archive name, i.e. "ar", "zip", "tar", "jar" or "cpio" 078 * @param in the input stream 079 * @return the archive input stream 080 * @throws ArchiveException if the archiver name is not known 081 * @throws IllegalArgumentException if the archiver name or stream is null 082 */ 083 public ArchiveInputStream createArchiveInputStream( 084 final String archiverName, final InputStream in) 085 throws ArchiveException { 086 if (archiverName == null || in == null) { 087 throw new IllegalArgumentException("Archivername must not be null."); 088 } 089 090 if ("ar".equalsIgnoreCase(archiverName)) { 091 return new ArArchiveInputStream(in); 092 } else if ("zip".equalsIgnoreCase(archiverName)) { 093 return new ZipArchiveInputStream(in); 094 } else if ("tar".equalsIgnoreCase(archiverName)) { 095 return new TarArchiveInputStream(in); 096 } else if ("jar".equalsIgnoreCase(archiverName)) { 097 return new JarArchiveInputStream(in); 098 } else if ("cpio".equalsIgnoreCase(archiverName)) { 099 return new CpioArchiveInputStream(in); 100 } 101 throw new ArchiveException("Archiver: " + archiverName + " not found."); 102 } 103 104 /** 105 * Create an archive output stream from an archiver name and an input stream. 106 * 107 * @param archiverName the archive name, i.e. "ar", "zip", "tar", "jar" or "cpio" 108 * @param out the output stream 109 * @return the archive output stream 110 * @throws ArchiveException if the archiver name is not known 111 * @throws IllegalArgumentException if the archiver name or stream is null 112 */ 113 public ArchiveOutputStream createArchiveOutputStream( 114 final String archiverName, final OutputStream out) 115 throws ArchiveException { 116 if (archiverName == null || out == null) { 117 throw new IllegalArgumentException( 118 "Archivername and stream must not be null."); 119 } 120 121 if ("ar".equalsIgnoreCase(archiverName)) { 122 return new ArArchiveOutputStream(out); 123 } else if ("zip".equalsIgnoreCase(archiverName)) { 124 return new ZipArchiveOutputStream(out); 125 } else if ("tar".equalsIgnoreCase(archiverName)) { 126 return new TarArchiveOutputStream(out); 127 } else if ("jar".equalsIgnoreCase(archiverName)) { 128 return new JarArchiveOutputStream(out); 129 } else if ("cpio".equalsIgnoreCase(archiverName)) { 130 return new CpioArchiveOutputStream(out); 131 } 132 throw new ArchiveException("Archiver: " + archiverName + " not found."); 133 } 134 135 /** 136 * Create an archive input stream from an input stream, autodetecting 137 * the archive type from the first few bytes of the stream. The InputStream 138 * must support marks, like BufferedInputStream. 139 * 140 * @param in the input stream 141 * @return the archive input stream 142 * @throws ArchiveException if the archiver name is not known 143 * @throws IllegalArgumentException if the stream is null or does not support mark 144 */ 145 public ArchiveInputStream createArchiveInputStream(final InputStream in) 146 throws ArchiveException { 147 if (in == null) { 148 throw new IllegalArgumentException("Stream must not be null."); 149 } 150 151 if (!in.markSupported()) { 152 throw new IllegalArgumentException("Mark is not supported."); 153 } 154 155 final byte[] signature = new byte[12]; 156 in.mark(signature.length); 157 try { 158 int signatureLength = in.read(signature); 159 in.reset(); 160 if (ZipArchiveInputStream.matches(signature, signatureLength)) { 161 return new ZipArchiveInputStream(in); 162 } else if (JarArchiveInputStream.matches(signature, signatureLength)) { 163 return new JarArchiveInputStream(in); 164 } else if (ArArchiveInputStream.matches(signature, signatureLength)) { 165 return new ArArchiveInputStream(in); 166 } else if (CpioArchiveInputStream.matches(signature, signatureLength)) { 167 return new CpioArchiveInputStream(in); 168 } 169 // Tar needs a bigger buffer to check the signature; read the first block 170 final byte[] tarheader = new byte[512]; 171 in.mark(tarheader.length); 172 signatureLength = in.read(tarheader); 173 in.reset(); 174 if (TarArchiveInputStream.matches(tarheader, signatureLength)) { 175 return new TarArchiveInputStream(in); 176 } 177 } catch (IOException e) { 178 throw new ArchiveException("Could not use reset and mark operations.", e); 179 } 180 181 throw new ArchiveException("No Archiver found for the stream signature"); 182 } 183 }