package org.nuxeo.ecm.webapp.clipboard;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipOutputStream;
import org.nuxeo.common.utils.StringUtils;
import org.nuxeo.ecm.core.api.Blob;
import org.nuxeo.ecm.core.api.ClientException;
import org.nuxeo.ecm.core.api.CoreSession;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.LifeCycleConstants;
import org.nuxeo.ecm.core.api.blobholder.BlobHolder;
import org.nuxeo.ecm.core.api.impl.blob.StringBlob;
import org.nuxeo.runtime.api.Framework;
public class DocumentListZipExporter {
public static final String ZIP_ENTRY_ENCODING_PROPERTY = "zip.entry.encoding";
public static enum ZIP_ENTRY_ENCODING_OPTIONS {
ascii
}
private static final int BUFFER = 2048;
private static final String SUMMARY_FILENAME = "INDEX.txt";
public File exportWorklistAsZip(List<DocumentModel> documents,
CoreSession documentManager, boolean exportAllBlobs)
throws ClientException, IOException {
StringBuilder blobList = new StringBuilder();
File tmpFile = File.createTempFile("NX-BigZipFile-", ".zip");
tmpFile.deleteOnExit(); // file is deleted after being downloaded in
// DownloadServlet
FileOutputStream fout = new FileOutputStream(tmpFile);
ZipOutputStream out = new ZipOutputStream(fout);
out.setMethod(ZipOutputStream.DEFLATED);
out.setLevel(9);
byte[] data = new byte[BUFFER];
for (DocumentModel doc : documents) {
// first check if DM is attached to the core
if (doc.getSessionId() == null) {
// refetch the doc from the core
doc = documentManager.getDocument(doc.getRef());
}
// NXP-2334 : skip deleted docs
if (LifeCycleConstants.DELETED_STATE.equals(doc.getCurrentLifeCycleState())) {
continue;
}
BlobHolder bh = doc.getAdapter(BlobHolder.class);
if (doc.isFolder() && !isEmptyFolder(doc, documentManager)) {
addFolderToZip("", out, doc, data, documentManager, blobList,
exportAllBlobs);
} else if (bh != null) {
addBlobHolderToZip("", out, doc, data, blobList, bh,
exportAllBlobs);
}
}
if (blobList.length() > 1) {
addSummaryToZip(out, data, blobList);
}
try {
out.close();
fout.close();
} catch (ZipException e) {
return null;
}
return tmpFile;
}
private void addFolderToZip(String path, ZipOutputStream out,
DocumentModel doc, byte[] data, CoreSession documentManager,
StringBuilder blobList, boolean exportAllBlobs)
throws ClientException, IOException {
String title = doc.getTitle();
List<DocumentModel> docList = documentManager.getChildren(doc.getRef());
for (DocumentModel docChild : docList) {
// NXP-2334 : skip deleted docs
if (LifeCycleConstants.DELETED_STATE.equals(docChild.getCurrentLifeCycleState())) {
continue;
}
BlobHolder bh = docChild.getAdapter(BlobHolder.class);
String newPath = null;
if (path.length() == 0) {
newPath = title;
} else {
newPath = path + "/" + title;
}
if (docChild.isFolder()
&& !isEmptyFolder(docChild, documentManager)) {
addFolderToZip(newPath, out, docChild, data, documentManager,
blobList, exportAllBlobs);
} else if (bh != null) {
addBlobHolderToZip(newPath, out, docChild, data, blobList, bh,
exportAllBlobs);
}
}
}
private boolean isEmptyFolder(DocumentModel doc, CoreSession documentManager)
throws ClientException {
List<DocumentModel> docList = documentManager.getChildren(doc.getRef());
for (DocumentModel docChild : docList) {
// If there is a blob or a folder, it is not empty.
if (docChild.getAdapter(BlobHolder.class) != null
|| docChild.isFolder()) {
return false;
}
}
return true;
}
/**
* Writes a summary file and puts it in the archive.
*/
private void addSummaryToZip(ZipOutputStream out, byte[] data,
StringBuilder sb) throws IOException {
Blob content = new StringBlob(sb.toString());
BufferedInputStream buffi = new BufferedInputStream(
content.getStream(), BUFFER);
ZipEntry entry = new ZipEntry(SUMMARY_FILENAME);
out.putNextEntry(entry);
int count = buffi.read(data, 0, BUFFER);
while (count != -1) {
out.write(data, 0, count);
count = buffi.read(data, 0, BUFFER);
}
out.closeEntry();
buffi.close();
}
private void addBlobHolderToZip(String path, ZipOutputStream out,
DocumentModel doc, byte[] data, StringBuilder blobList,
BlobHolder bh, boolean exportAllBlobs) throws IOException,
ClientException {
List<Blob> blobs = new ArrayList<Blob>();
if (exportAllBlobs) {
if (bh.getBlobs() != null) {
blobs = bh.getBlobs();
}
} else {
Blob mainBlob = bh.getBlob();
if (mainBlob != null) {
blobs.add(mainBlob);
}
}
if (blobs.size() > 0) { // add document info
SimpleDateFormat format = new SimpleDateFormat(
"dd-MM-yyyy HH:mm:ss");
if (path.length() > 0) {
blobList.append(path).append('/');
}
blobList.append(doc.getTitle()).append(" ");
blobList.append(doc.getType()).append(" ");
Calendar c = (Calendar) doc.getPropertyValue("dc:modified");
if (c != null) {
blobList.append(format.format(c.getTime()));
}
blobList.append("\n");
}
for (Blob content : blobs) {
String fileName = content.getFilename();
if (fileName == null) {
// use a default value
fileName = "file.bin";
}
BufferedInputStream buffi = new BufferedInputStream(
content.getStream(), BUFFER);
// Workaround to deal with duplicate file names.
int tryCount = 0;
String entryPath = null;
String entryName = null;
while (true) {
try {
ZipEntry entry = null;
if (tryCount == 0) {
entryName = fileName;
} else {
entryName = formatFileName(fileName, "(" + tryCount
+ ")");
}
if (path.length() == 0) {
entryPath = entryName;
} else {
entryPath = path + "/" + entryName;
}
entryPath = escapeEntryPath(entryPath);
entry = new ZipEntry(entryPath);
out.putNextEntry(entry);
break;
} catch (ZipException e) {
tryCount++;
}
}
blobList.append(" - ").append(entryName).append("\n");
int count = buffi.read(data, 0, BUFFER);
while (count != -1) {
out.write(data, 0, count);
count = buffi.read(data, 0, BUFFER);
}
out.closeEntry();
buffi.close();
}
}
private String formatFileName(String filename, String count) {
StringBuilder sb = new StringBuilder();
CharSequence name = filename.subSequence(0, filename.lastIndexOf("."));
CharSequence extension = filename.subSequence(
filename.lastIndexOf("."), filename.length());
sb.append(name).append(count).append(extension);
return sb.toString();
}
protected String escapeEntryPath(String path) {
String zipEntryEncoding = Framework.getProperty(ZIP_ENTRY_ENCODING_PROPERTY);
if (zipEntryEncoding != null
&& zipEntryEncoding.equals(ZIP_ENTRY_ENCODING_OPTIONS.ascii.toString())) {
return StringUtils.toAscii(path);
}
return path;
}
}