001/*
002 * (C) Copyright 2017 Nuxeo (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Guillaume Renard <grenard@nuxeo.com>
018 */
019package org.nuxeo.ecm.core.utils;
020
021import java.io.File;
022import java.io.FileOutputStream;
023import java.io.IOException;
024import java.io.InputStream;
025import java.util.Collection;
026import java.util.HashSet;
027import java.util.List;
028import java.util.zip.ZipOutputStream;
029
030import org.nuxeo.common.utils.StringUtils;
031import org.nuxeo.common.utils.ZipUtils;
032import org.nuxeo.ecm.core.api.Blob;
033import org.nuxeo.ecm.core.api.Blobs;
034import org.nuxeo.runtime.api.Framework;
035
036/**
037 * Blob utility methods.
038 * 
039 * @since 9.3
040 */
041public class BlobUtils {
042
043    public static enum ZIP_ENTRY_ENCODING_OPTIONS {
044        ascii
045    }
046
047    public static final String ZIP_ENTRY_ENCODING_PROPERTY = "zip.entry.encoding";
048
049    protected static String escapeEntryPath(String path) {
050        String zipEntryEncoding = Framework.getProperty(ZIP_ENTRY_ENCODING_PROPERTY);
051        if (zipEntryEncoding != null && zipEntryEncoding.equals(ZIP_ENTRY_ENCODING_OPTIONS.ascii.toString())) {
052            return StringUtils.toAscii(path, true);
053        }
054        return path;
055    }
056
057    protected static String getFileName(Blob blob) {
058        String entry = blob.getFilename();
059        if (entry == null) {
060            entry = "Unknown_" + System.identityHashCode(blob);
061        }
062        return escapeEntryPath(entry);
063    }
064
065    /**
066     * Zip the given blob.
067     * 
068     * @param blob the blob
069     * @param filename if no filename is given, the blob's filename will be used
070     * @return a zip containing the blob
071     * @throws IOException
072     */
073    public static Blob zip(Blob blob, String filename) throws IOException {
074        if (filename == null || (filename = filename.trim()).length() == 0) {
075            filename = blob.getFilename();
076        }
077        File file = Framework.createTempFile("nxops-createzip-", ".tmp");
078        ZipOutputStream out = new ZipOutputStream(new FileOutputStream(file));
079        Framework.trackFile(file, file);
080        try {
081            zip(blob, out);
082        } finally {
083            out.finish();
084            out.close();
085        }
086        return Blobs.createBlob(file, "application/zip", null, filename);
087    }
088
089    protected static void zip(Blob blob, ZipOutputStream out) throws IOException {
090        String entry = getFileName(blob);
091        InputStream in = blob.getStream();
092        try {
093            ZipUtils._zip(entry, in, out);
094        } finally {
095            in.close();
096        }
097    }
098
099    /**
100     * Zip a list of blob.
101     * 
102     * @param blobs the blob list
103     * @param fileName if no filename is given, the first blob's filename will be used
104     * @return a zip containing the list of blob
105     * @throws IOException
106     */
107    public static Blob zip(List<Blob> blobs, String fileName) throws IOException {
108        if (fileName == null || (fileName = fileName.trim()).length() == 0) {
109            fileName = blobs.isEmpty() ? null : blobs.get(0).getFilename();
110        }
111        File file = Framework.createTempFile("nxops-createzip-", ".tmp");
112        ZipOutputStream out = new ZipOutputStream(new FileOutputStream(file));
113        Framework.trackFile(file, file);
114        try {
115            zip(blobs, out);
116        } finally {
117            out.finish();
118            out.close();
119        }
120        return Blobs.createBlob(file, "application/zip", null, fileName);
121    }
122
123    protected static void zip(List<Blob> blobs, ZipOutputStream out) throws IOException {
124        // use a set to avoid zipping entries with same names
125        Collection<String> names = new HashSet<String>();
126        int cnt = 1;
127        for (Blob blob : blobs) {
128            String entry = getFileName(blob);
129            if (!names.add(entry)) {
130                entry = "renamed_" + (cnt++) + "_" + entry;
131            }
132            InputStream in = blob.getStream();
133            try {
134                ZipUtils._zip(entry, in, out);
135            } finally {
136                in.close();
137            }
138        }
139    }
140
141}