// *************************************************************************************************************************** | |
// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * | |
// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * | |
// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * | |
// * with the License. You may obtain a copy of the License at * | |
// * * | |
// * http://www.apache.org/licenses/LICENSE-2.0 * | |
// * * | |
// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * | |
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * | |
// * specific language governing permissions and limitations under the License. * | |
// *************************************************************************************************************************** | |
package org.apache.juneau.utils; | |
import java.io.*; | |
import java.util.*; | |
import java.util.zip.*; | |
import org.apache.juneau.*; | |
import org.apache.juneau.http.*; | |
import org.apache.juneau.http.annotation.*; | |
/** | |
* Utility class for representing the contents of a zip file as a list of entries whose contents don't resolve until | |
* serialization time. | |
* | |
* <p> | |
* Generally associated with <c>RestServlets</c> using the <c>responseHandlers</c> annotation so that | |
* REST methods can easily create ZIP file responses by simply returning instances of this class. | |
*/ | |
@Response | |
public class ZipFileList extends LinkedList<ZipFileList.ZipFileEntry> implements Streamable { | |
private static final long serialVersionUID = 1L; | |
/** | |
* The name of the zip file. | |
*/ | |
public final String fileName; | |
@Header("Content-Type") | |
@Override /* Streamable */ | |
public MediaType getMediaType() { | |
return MediaType.forString("application/zip"); | |
} | |
/** | |
* Returns the value for the <c>Content-Disposition</c> header. | |
* | |
* @return The value for the <c>Content-Disposition</c> header. | |
*/ | |
@Header("Content-Disposition") | |
public String getContentDisposition() { | |
return "attachment;filename=" + fileName; | |
} | |
@ResponseBody | |
@Override /* Streamable */ | |
public void streamTo(OutputStream os) throws IOException { | |
try (ZipOutputStream zos = new ZipOutputStream(os)) { | |
for (ZipFileEntry e : this) | |
e.write(zos); | |
} | |
os.flush(); | |
} | |
/** | |
* Constructor. | |
* | |
* @param fileName The file name of the zip file to create. | |
*/ | |
public ZipFileList(String fileName) { | |
this.fileName = fileName; | |
} | |
/** | |
* Add an entry to this list. | |
* | |
* @param e The zip file entry. | |
* @return This object (for method chaining). | |
*/ | |
public ZipFileList append(ZipFileEntry e) { | |
add(e); | |
return this; | |
} | |
/** | |
* Interface for ZipFileList entries. | |
*/ | |
public static interface ZipFileEntry { | |
/** | |
* Write this entry to the specified output stream. | |
* | |
* @param zos The output stream to write to. | |
* @throws IOException Thrown by underlying stream. | |
*/ | |
void write(ZipOutputStream zos) throws IOException; | |
} | |
/** | |
* ZipFileList entry for File entry types. | |
*/ | |
public static class FileEntry implements ZipFileEntry { | |
/** The root file to base the entry paths on. */ | |
protected File root; | |
/** The file being zipped. */ | |
protected File file; | |
/** | |
* Constructor. | |
* | |
* @param root The root file that represents the base path. | |
* @param file The file to add to the zip file. | |
*/ | |
public FileEntry(File root, File file) { | |
this.root = root; | |
this.file = file; | |
} | |
/** | |
* Constructor. | |
* | |
* @param file The file to add to the zip file. | |
*/ | |
public FileEntry(File file) { | |
this.file = file; | |
this.root = (file.isDirectory() ? file : file.getParentFile()); | |
} | |
@Override /* ZipFileEntry */ | |
public void write(ZipOutputStream zos) throws IOException { | |
addFile(zos, file); | |
} | |
/** | |
* Subclasses can override this method to customize which files get added to a zip file. | |
* | |
* @param f The file being added to the zip file. | |
* @return Always returns <jk>true</jk>. | |
*/ | |
public boolean doAdd(File f) { | |
return true; | |
} | |
/** | |
* Adds the specified file to the specified output stream. | |
* | |
* @param zos The output stream. | |
* @param f The file to add. | |
* @throws IOException Thrown by underlying stream. | |
*/ | |
protected void addFile(ZipOutputStream zos, File f) throws IOException { | |
if (doAdd(f)) { | |
if (f.isDirectory()) { | |
File[] fileList = f.listFiles(); | |
if (fileList == null) | |
throw new IOException(f.toString()); | |
for (File fc : fileList) | |
addFile(zos, fc); | |
} else if (f.canRead()) { | |
String path = f.getAbsolutePath().substring(root.getAbsolutePath().length() + 1).replace('\\', '/'); | |
ZipEntry e = new ZipEntry(path); | |
e.setSize(f.length()); | |
zos.putNextEntry(e); | |
try (FileInputStream fis = new FileInputStream(f)) { | |
IOPipe.create(fis, zos).run(); | |
} | |
} | |
} | |
} | |
} | |
} |