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 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     */
072    public static Blob zip(Blob blob, String filename) throws IOException {
073        if (filename == null || (filename = filename.trim()).length() == 0) {
074            filename = blob.getFilename();
075        }
076        File file = Framework.createTempFile("nxops-createzip-", ".tmp");
077        ZipOutputStream out = new ZipOutputStream(new FileOutputStream(file));
078        Framework.trackFile(file, file);
079        try {
080            zip(blob, out);
081        } finally {
082            out.finish();
083            out.close();
084        }
085        return Blobs.createBlob(file, "application/zip", null, filename);
086    }
087
088    protected static void zip(Blob blob, ZipOutputStream out) throws IOException {
089        String entry = getFileName(blob);
090        try (InputStream in = blob.getStream()) {
091            ZipUtils._zip(entry, in, out);
092        }
093    }
094
095    /**
096     * Zip a list of blob.
097     *
098     * @param blobs the blob list
099     * @param fileName if no filename is given, the first blob's filename will be used
100     * @return a zip containing the list of blob
101     */
102    public static Blob zip(List<Blob> blobs, String fileName) throws IOException {
103        if (fileName == null || (fileName = fileName.trim()).length() == 0) {
104            fileName = blobs.isEmpty() ? null : blobs.get(0).getFilename();
105        }
106        File file = Framework.createTempFile("nxops-createzip-", ".tmp");
107        ZipOutputStream out = new ZipOutputStream(new FileOutputStream(file));
108        Framework.trackFile(file, file);
109        try {
110            zip(blobs, out);
111        } finally {
112            out.finish();
113            out.close();
114        }
115        return Blobs.createBlob(file, "application/zip", null, fileName);
116    }
117
118    protected static void zip(List<Blob> blobs, ZipOutputStream out) throws IOException {
119        // use a set to avoid zipping entries with same names
120        Collection<String> names = new HashSet<>();
121        int cnt = 1;
122        for (Blob blob : blobs) {
123            String entry = getFileName(blob);
124            if (!names.add(entry)) {
125                entry = "renamed_" + (cnt++) + "_" + entry;
126            }
127            try (InputStream in = blob.getStream()) {
128                ZipUtils._zip(entry, in, out);
129            }
130        }
131    }
132
133}