de.fhg.iais.asc.xslt.binaries.DownloadAndScaleBinary.java Source code

Java tutorial

Introduction

Here is the source code for de.fhg.iais.asc.xslt.binaries.DownloadAndScaleBinary.java

Source

package de.fhg.iais.asc.xslt.binaries;

/******************************************************************************
 * Copyright 2011 (c) Fraunhofer IAIS Netmedia  http://www.iais.fraunhofer.de *
 * ************************************************************************** *
 * Licensed 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.                                             *
 ******************************************************************************/

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.tika.detect.Detector;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.metadata.TikaMetadataKeys;
import org.apache.tika.mime.MediaType;
import org.apache.tika.parser.AutoDetectParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.fhg.iais.asc.commons.exceptions.AscDataErrorException;
import de.fhg.iais.asc.commons.exceptions.AscTechnicalErrorException;
import de.fhg.iais.asc.xslt.binaries.download.Downloader;
import de.fhg.iais.asc.xslt.binaries.scale.BinaryImageSize;
import de.fhg.iais.asc.xslt.binaries.scale.conf.PreviewImageType;
import eu.medsea.mimeutil.MimeException;
import eu.medsea.mimeutil.MimeType;
import eu.medsea.mimeutil.MimeUtil;
import eu.medsea.mimeutil.MimeUtil2;

public class DownloadAndScaleBinary {
    private static final String MSG_CANT_CREATE_LOCAL_PATH = "Can't create path for URI \"{}\"";
    private static final Logger LOG = LoggerFactory.getLogger(DownloadAndScaleBinary.class);
    private static final String ORIGINAL_PREFIX = "orig/";

    static final String CTXKEY_PATH_CONVERT = "convert_location";

    private static final boolean DOWNLOAD_PERMANENT_ONLY = true;
    private static final boolean DOWNLOAD_ANY = false;

    private final DownloadAndScaleContext context;
    private String mimeType;

    private final boolean primary;
    private final boolean full;

    private final URI downloadableURI;
    private final String typeSubPath;
    private final File permanentOriginal;

    private File locallyAvailableOriginal; // locally available or null

    private BinaryImageSize binaryImageSize;

    // MimeUtil2 eats up memory on each instantiation and it doesnt get released during runtime
    // static object helps here
    private static MimeUtil2 mimeUtil;
    static {
        mimeUtil = new MimeUtil2();
        mimeUtil.registerMimeDetector("eu.medsea.mimeutil.detector.ExtensionMimeDetector");
        mimeUtil.registerMimeDetector("eu.medsea.mimeutil.detector.MagicMimeMimeDetector");
    }

    public DownloadAndScaleBinary(DownloadAndScaleContext context, String pathOrUrl, String localPath,
            boolean primary, boolean full) {
        this.context = context;
        this.primary = primary;
        this.full = full;

        this.downloadableURI = Downloader.createDownloadableURI(pathOrUrl);
        if (this.downloadableURI == null) {
            this.typeSubPath = pathOrUrl;
        } else {
            this.typeSubPath = findLocalPath(localPath, this.downloadableURI);
        }

        File binaryRoot = this.context.getBinaryRoot();
        this.permanentOriginal = new File(binaryRoot, ORIGINAL_PREFIX + this.typeSubPath);

        if (this.permanentOriginal.isFile() || moveToPermanentOriginal()) {
            this.locallyAvailableOriginal = this.permanentOriginal;
        }
    }

    public DownloadAndScaleBinary(DownloadAndScaleContext context, URI uri, String localPathname, boolean primary,
            boolean full) {
        this.context = context;
        this.primary = primary;
        this.full = full;

        this.downloadableURI = uri;
        this.typeSubPath = determineTypeSubPath(uri, localPathname);

        File binaryRoot = this.context.getBinaryRoot();
        this.permanentOriginal = new File(binaryRoot, ORIGINAL_PREFIX + this.typeSubPath);

        if (this.permanentOriginal.isFile() || moveToPermanentOriginal()) {
            this.locallyAvailableOriginal = this.permanentOriginal;
        }
    }

    public String getTypeSubPath() {
        return this.typeSubPath;
    }

    private String determineTypeSubPath(URI uri, String localPathname) {
        if (StringUtils.isNotEmpty(localPathname)) {
            return localPathname;
        }

        if (uri != null) {
            String fromURI = Downloader.createPathFromURI(uri);

            if (StringUtils.isNotEmpty(fromURI)) {
                return fromURI;
            }
        }

        final String msg = String.format(MSG_CANT_CREATE_LOCAL_PATH, ObjectUtils.toString(uri));
        throw new AscDataErrorException(msg);
    }

    private static String findLocalPath(String localPath, URI uri) {
        return StringUtils.isNotEmpty(localPath) ? localPath : Downloader.createPathFromURI(uri);
    }

    public DownloadAndScaleBinary setMimeType(String mimeType) { // from mapper
        this.mimeType = mimeType;
        return this;
    }

    public String getMimeType() { // from mapper or tika if the mapper didn't specify the mimetype
        return this.mimeType;
    }

    private boolean moveToPermanentOriginal() {
        File binaryRoot = this.context.getBinaryRoot();
        File f = new File(binaryRoot, this.typeSubPath);
        if (f.isFile()) {
            try {
                FileUtils.moveFile(f, this.permanentOriginal);
                return true;
            } catch (IOException e) {
                String srcPath = f.getAbsolutePath();
                String destPath = this.permanentOriginal.getAbsolutePath();
                String msg = String.format("Can't move file \"%s\" to \"%s\"", srcPath, destPath);
                throw new AscTechnicalErrorException(msg);
            }
        }

        return false;
    }

    public List<Object> createAllVariants(List<PreviewImageType> previewTypes) {
        List<Object> result = new ArrayList<Object>();

        if (this.context.isTypeAllowed("orig")) {
            makeOriginalLocallyAvailable(DOWNLOAD_PERMANENT_ONLY);
        }

        if ((this.mimeType == null) || (this.mimeType.equals(""))) {
            this.mimeType = detectMimetype();

            if (this.mimeType.equals("video/quicktime")) {
                this.mimeType = "video/mp4";
            }
        }

        if (this.mimeType.startsWith("audio/") || this.mimeType.startsWith("video/")
                || this.mimeType.equals("application/pdf")) { // is audio, video or pdf
            if (this.mimeType.equals("video/vimeo")) { // if external video
                result.add(this.typeSubPath);
            } else if (this.locallyAvailableOriginal == this.permanentOriginal) {
                String filepath = "full" + "/" + this.typeSubPath;
                result.add(filepath);
                File binary = new File(this.context.getBinaryRoot() + "/" + filepath);
                try {
                    FileUtils.copyFile(this.locallyAvailableOriginal, binary);

                } catch (IOException e) {
                    LOG.error("Can't create directory 'full': " + e);

                }
            } else if (this.downloadableURI != null) {
                result.add(this.downloadableURI);
            }
        } else if (!this.mimeType.startsWith("image/")) {
            if (this.locallyAvailableOriginal != null) {
                this.locallyAvailableOriginal.delete();
            }
        }

        if ((this.mimeType.startsWith("image/") || (this.mimeType.equals("application/pdf")))) { // is image or pdf
            if (this.context.isAnyScaleAllowed()) {
                for (PreviewImageType previewType : previewTypes) {
                    if (isPreviewAllowed(previewType)) {
                        final String previewTypeName = previewType.getType();
                        String scaled = previewTypeName + "/" + FilenameUtils.removeExtension(this.typeSubPath)
                                + ".jpg";
                        if (executeScale(previewType, scaled, this.mimeType)) {
                            result.add(scaled);
                        } else { // delete file if scaling could not be executed (e.g. damaged image)
                            if (this.locallyAvailableOriginal != null) {
                                this.locallyAvailableOriginal.delete();
                            }
                        }
                    }
                }
            }
        }

        return result;
    }

    private String detectMimetype() {
        String mimetype = "";

        if (this.locallyAvailableOriginal == null) {
            return mimetype;
        }

        FileInputStream is = null;
        BufferedInputStream bis = null;
        MediaType mediaType = null;

        try {
            try {
                is = new FileInputStream(this.locallyAvailableOriginal);
                bis = new BufferedInputStream(is);
                AutoDetectParser parser = new AutoDetectParser();
                Detector detector = parser.getDetector();
                Metadata md = new Metadata();
                md.add(TikaMetadataKeys.RESOURCE_NAME_KEY, this.locallyAvailableOriginal.getName());
                mediaType = detector.detect(bis, md);
            } catch (FileNotFoundException e) {
                LOG.error("Can't find file: \"" + this.locallyAvailableOriginal.getAbsolutePath(), e);
            }
        } catch (IOException e) {
            LOG.error("Can't read file: \"" + this.locallyAvailableOriginal.getAbsolutePath(), e);
        } finally {
            IOUtils.closeQuietly(bis);
            IOUtils.closeQuietly(is);
        }

        return mediaType.toString();
    }

    private boolean executeScale(PreviewImageType previewType, String scaled, String mimetype) {
        if (null == makeOriginalLocallyAvailable(DOWNLOAD_ANY)) {
            return false;
        }

        return this.context.createScaled(this.locallyAvailableOriginal, scaled, previewType, mimetype);
    }

    private boolean isPreviewAllowed(PreviewImageType previewType) {
        String typeName = previewType.getType();

        if (this.mimeType.equals("application/pdf") && typeName.equals("full")) {
            return false;
        }

        if (typeName.equals("full") && !this.full) {
            return false;
        }

        return previewType.isAllowedForPrimaryValue(this.primary) && this.context.isTypeAllowed(typeName)
                && deepCheckIsImage() && checkMinimumOriginalSize(previewType);
    }

    private boolean isImageWithDefault(boolean resultForEmptyMimeType) {
        String mimeType = this.mimeType;
        return StringUtils.isEmpty(mimeType) ? resultForEmptyMimeType
                : (mimeType.startsWith("image/") || (this.mimeType.equals("application/pdf")));
    }

    private boolean deepCheckIsImage() {
        if (StringUtils.isEmpty(this.mimeType)) {
            if (null != makeOriginalLocallyAvailable(DOWNLOAD_ANY)) {
                try {
                    synchronized (mimeUtil) {
                        Collection<?> mimeTypes = mimeUtil.getMimeTypes(this.locallyAvailableOriginal);
                        MimeType type = MimeUtil.getMostSpecificMimeType(mimeTypes);
                        this.mimeType = type.toString();
                    }
                } catch (MimeException e) {
                    LOG.warn("Can't detect MIME type for file \"" + this.locallyAvailableOriginal.getAbsolutePath()
                            + "\"", e);
                }
            }
        }

        return isImageWithDefault(false);
    }

    private boolean checkMinimumOriginalSize(PreviewImageType previewType) {

        BinaryImageSize minOriginalSize = previewType.getMinOriginalSize();
        if (minOriginalSize == null) {
            return true;
        }

        if (this.mimeType.equals("application/pdf")) {
            return true;
        }

        final BinaryImageSize binaryImageSize = determineBinaryImageSize();
        if (binaryImageSize == null) {
            return false;
        }

        return (binaryImageSize.getWidth() >= minOriginalSize.getWidth())
                || (binaryImageSize.getHeight() >= minOriginalSize.getHeight());
    }

    private BinaryImageSize determineBinaryImageSize() {
        if ((this.binaryImageSize == null) && deepCheckIsImage()) {
            if (null != makeOriginalLocallyAvailable(DOWNLOAD_ANY)) {
                try {
                    this.binaryImageSize = BinaryImageSize.of(this.locallyAvailableOriginal);
                } catch (IOException e) {
                    String path = this.locallyAvailableOriginal.getAbsolutePath();
                    LOG.error("Can't read image size of \"" + path + "\"", e);
                }
            }
        }

        return this.binaryImageSize;
    }

    public File makeOriginalLocallyAvailable(boolean permanentOnly) {
        if ((this.downloadableURI != null) && (this.locallyAvailableOriginal == null)) {
            this.locallyAvailableOriginal = this.context.downloadFile(this.downloadableURI, this.permanentOriginal,
                    permanentOnly);
        }

        return this.locallyAvailableOriginal;
    }
}