io.stallion.contentPublishing.UploadRequestProcessor.java Source code

Java tutorial

Introduction

Here is the source code for io.stallion.contentPublishing.UploadRequestProcessor.java

Source

/*
 * Stallion Core: A Modern Web Framework
 *
 * Copyright (C) 2015 - 2016 Stallion Software LLC.
 *
 * This program is free software: you can redistribute it and/or modify it under the terms of the
 * GNU General Public License as published by the Free Software Foundation, either version 2 of
 * the License, or (at your option) any later version. This program is distributed in the hope that
 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
 * License for more details. You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/gpl-2.0.html>.
 *
 *
 *
 */

package io.stallion.contentPublishing;

import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URI;

import static io.stallion.utils.Literals.*;

import com.mashape.unirest.http.Unirest;
import com.mashape.unirest.http.exceptions.UnirestException;
import io.stallion.Context;
import io.stallion.dataAccess.DataAccessRegistry;
import io.stallion.exceptions.ClientException;
import io.stallion.exceptions.WebException;
import io.stallion.reflection.PropertyUtils;
import io.stallion.requests.IRequest;
import io.stallion.services.CloudStorageService;
import io.stallion.services.Log;
import io.stallion.settings.Settings;
import io.stallion.settings.childSections.UploadStorageType;
import io.stallion.utils.DateUtils;
import io.stallion.utils.GeneralUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.imgscalr.Scalr;
import org.parboiled.common.FileUtils;

import javax.imageio.ImageIO;
import javax.servlet.http.Part;

public class UploadRequestProcessor<U extends UploadedFile> {
    private IRequest stRequest;

    private String uploadsFolder;
    private UploadedFileController<U> fileController;
    private String urlToFetch;

    protected IRequest getStRequest() {
        return stRequest;
    }

    protected String getUploadsFolder() {
        return uploadsFolder;
    }

    protected UploadedFileController<U> getFileController() {
        return fileController;
    }

    public UploadRequestProcessor(String uploadsFolder) {
        this(uploadsFolder, UploadedFileController.instance());
    }

    public UploadRequestProcessor(String uploadsFolder, UploadedFileController<U> fileController) {
        this.stRequest = stRequest;
        if (!uploadsFolder.endsWith("/")) {
            uploadsFolder += "/";
        }
        this.uploadsFolder = uploadsFolder;
        this.fileController = fileController;

    }

    public U addFromUrl(String url, boolean isPublic) {
        this.urlToFetch = url;
        U uploaded = newUploadedFileInstance(isPublic);
        File file = downloadExternalToLocalFile(uploaded);
        if ("image".equals(uploaded.getType())
                && Settings.instance().getUserUploads().getGenerateImageThumbnails() == true) {
            generateImageSizes(uploaded, file.getAbsolutePath());
        }
        if (Settings.instance().getUserUploads().getStorageType().equals(UploadStorageType.Cloud)) {
            transferAllToS3(uploaded);
        }
        uploaded.setProvisional(false);
        fileController.save(uploaded);
        return uploaded;
    }

    public U upload(IRequest stRequest) {
        this.stRequest = stRequest;
        try {
            return doUpload();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    protected U doUpload() throws IOException {
        U uploaded = newUploadedFileInstance(
                "true".equals(stRequest.getQueryParams().getOrDefault("stUploadIsPublic", "")));
        File file = writeMultiPartToLocalFile(uploaded);
        if ("image".equals(uploaded.getType())
                && Settings.instance().getUserUploads().getGenerateImageThumbnails() == true) {
            generateImageSizes(uploaded, file.getAbsolutePath());
        }
        if (Settings.instance().getUserUploads().getStorageType().equals(UploadStorageType.Cloud)) {
            transferAllToS3(uploaded);
        }
        uploaded.setProvisional(false);
        fileController.save(uploaded);
        return uploaded;
    }

    protected File downloadExternalToLocalFile(U uploaded) {

        URI uri = URI.create(urlToFetch);

        String fullFileName = FilenameUtils.getName(uri.getPath());
        String extension = FilenameUtils.getExtension(fullFileName);
        String fileName = truncate(fullFileName, 85);
        String relativePath = GeneralUtils.slugify(truncate(FilenameUtils.getBaseName(fullFileName), 75)) + "-"
                + DateUtils.mils() + "." + extension;
        relativePath = "stallion-file-" + uploaded.getId() + "/" + GeneralUtils.secureRandomToken(8) + "/"
                + relativePath;

        String destPath = uploadsFolder + relativePath;
        try {
            FileUtils.forceMkdir(new File(destPath).getParentFile());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        uploaded.setCloudKey(relativePath).setExtension(extension).setName(fileName)
                .setOwnerId(Context.getUser().getId()).setUploadedAt(DateUtils.utcNow())
                .setType(fileController.getTypeForExtension(extension));

        // Make raw URL
        String url = makeRawUrlForFile(uploaded, "org");
        uploaded.setRawUrl(url);

        fileController.save(uploaded);

        File outFile = new File(destPath);

        try {
            IOUtils.copy(Unirest.get(urlToFetch).asBinary().getRawBody(),
                    org.apache.commons.io.FileUtils.openOutputStream(outFile));
        } catch (UnirestException e) {
            throw new ClientException("Error downloading file from " + url + ": " + e.getMessage(), 400, e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        return outFile;

    }

    protected U newUploadedFileInstance(boolean isPublic) {
        U uploaded = fileController.newModel();
        if (!empty(uploaded.getId())) {
            throw new WebException("You cannot reuse the same upload file processor twice.");
        }
        Long id = DataAccessRegistry.instance().getTickets().nextId();
        String secret = GeneralUtils.randomTokenBase32(14);
        uploaded.setSecret(secret).setPubliclyViewable(isPublic)
                .setPubliclyViewable(Settings.instance().getUserUploads().getUploadsArePublic()).setId(id);
        fileController.save(uploaded);
        return uploaded;
    }

    protected File writeMultiPartToLocalFile(U uploaded) throws IOException {
        stRequest.setAsMultiPartRequest();

        final String path = stRequest.getParameter("destination");
        final Part filePart = stRequest.getPart("file");
        String fullFileName = getFileNameFromPart(filePart);
        String extension = FilenameUtils.getExtension(fullFileName);
        String fileName = truncate(fullFileName, 85);
        String relativePath = GeneralUtils.slugify(truncate(FilenameUtils.getBaseName(fullFileName), 75)) + "-"
                + DateUtils.mils() + "." + extension;
        relativePath = "stallion-file-" + uploaded.getId() + "/" + GeneralUtils.secureRandomToken(8) + "/"
                + relativePath;

        String destPath = uploadsFolder + relativePath;
        FileUtils.forceMkdir(new File(destPath).getParentFile());

        uploaded.setCloudKey(relativePath).setExtension(extension).setName(fileName)
                .setOwnerId(Context.getUser().getId()).setUploadedAt(DateUtils.utcNow())
                .setType(fileController.getTypeForExtension(extension));

        // Make raw URL
        String url = makeRawUrlForFile(uploaded, "org");
        uploaded.setRawUrl(url);

        fileController.save(uploaded);

        OutputStream out = null;
        InputStream filecontent = null;
        boolean failed = true;
        File outFile = new File(destPath);
        Long amountRead = 0L;
        Long maxSize = Settings.instance().getUserUploads().getMaxFileSizeBytes();
        try {
            out = new FileOutputStream(destPath);
            filecontent = filePart.getInputStream();

            int read = 0;
            final byte[] bytes = new byte[1024];
            while ((read = filecontent.read(bytes)) != -1) {
                amountRead += read;
                if (amountRead > maxSize) {
                    throw new ClientException("Uploaded file exceeded max size of " + maxSize + " bytes.");
                }
                out.write(bytes, 0, read);
            }
            failed = false;
            Log.info("File{0}being uploaded to {1}", new Object[] { fileName, path });
            uploaded.setSizeBytes(amountRead);
        } finally {
            if (out != null) {
                out.close();
            }
            if (filecontent != null) {
                filecontent.close();
            }
            if (failed) {
                if (outFile.exists()) {
                    outFile.delete();
                }
            }
        }
        return new File(destPath);
    }

    protected String makeRawUrlForFile(U uploadedFile, String size) {
        String endingSlug = GeneralUtils.slugify(truncate(FilenameUtils.getBaseName(uploadedFile.getName()), 50));
        if (!"org".equals(size)) {
            endingSlug += "." + size;
        }
        if (!empty(uploadedFile.getExtension())) {
            endingSlug += "." + uploadedFile.getExtension();
        }
        String url = "{cdnUrl}/st-user-uploads/view-file/" + uploadedFile.getSecret() + "/" + uploadedFile.getId()
                + "/org/" + endingSlug + "?ts=" + DateUtils.mils();
        return url;
    }

    protected void transferAllToS3(U uploaded) {
        for (String part : list("", "thumb", "medium", "small")) {
            transferToS3(uploaded, part);
        }
    }

    protected void transferToS3(U uploaded, String part) {
        String cloudKeyProperty = "cloudKey";
        String rawUrlProperty = "rawUrl";
        if (!empty(part)) {
            cloudKeyProperty = part + "CloudKey";
            rawUrlProperty = part + "RawUrl";
        }
        String cloudKey = (String) PropertyUtils.getProperty(uploaded, cloudKeyProperty);
        Log.info("Cloud key for {0} is {1}", cloudKeyProperty, cloudKey);
        if (empty(cloudKey)) {
            return;
        }
        File file = new File(uploadsFolder + cloudKey);
        if (!file.exists()) {
            Log.info("No file {0}", file.getAbsolutePath());
            return;
        }

        String newCloudKey = Settings.instance().getUserUploads().getUploadsPathPrefix() + cloudKey;
        if (newCloudKey.startsWith("/")) {
            newCloudKey = newCloudKey.substring(1);
        }
        Log.info("Upload to s3 file {0} bucket {1} key: {2}", file.getAbsolutePath(),
                Settings.instance().getUserUploads().getUploadsBucket(), newCloudKey);
        CloudStorageService.instance().uploadFile(file, Settings.instance().getUserUploads().getUploadsBucket(),
                newCloudKey, true);
        PropertyUtils.setProperty(uploaded, cloudKeyProperty, newCloudKey);
        String s3url;
        if (uploaded.isPubliclyViewable()) {
            s3url = or(Settings.instance().getUserUploads().getUploadsBucketBaseUrl(), CloudStorageService
                    .instance().getBucketBaseUrl(Settings.instance().getUserUploads().getUploadsBucket()));
            if (!s3url.endsWith("/")) {
                s3url = s3url + "/";

            }
            s3url = s3url + newCloudKey;
        } else {
            s3url = "{cdnUrl}/st-user-uploads/view-cloud-file/" + uploaded.getSecret() + "/" + uploaded.getId()
                    + "/" + part + "/" + newCloudKey + "?ts=" + DateUtils.mils();
        }
        PropertyUtils.setProperty(uploaded, rawUrlProperty, s3url);

        file.delete();

    }

    protected void generateImageSizes(U uploaded, String path) {
        try {
            File originalImageFile = new File(path);
            BufferedImage image = ImageIO.read(originalImageFile);
            if (image == null) {
                throw new ClientException("Could not interpret uploaded file as a valid image file.");
            }
            hydrateHeightAndWidth(uploaded, image);
            if (uploaded.getWidth() > 60) {
                createResized(uploaded, image, path, 120, 60, "thumb");
            }
            if (uploaded.getWidth() > 250) {
                createResized(uploaded, image, path, 550, 250, "small");
            }

            if (uploaded.getWidth() > 900) {
                createResized(uploaded, image, path, 1600, 900, "medium");
            }
            if (Settings.instance().getUserUploads().getResizeImagesToMax() > 0) {
                Scalr.Mode scaleMode = Scalr.Mode.FIT_TO_WIDTH;
                if (uploaded.getWidth() > Settings.instance().getUserUploads().getResizeImagesToMax()) {
                    createResized(uploaded, image, path,
                            Settings.instance().getUserUploads().getResizeImagesToMax(),
                            Settings.instance().getUserUploads().getResizeImagesToMax(), "org", scaleMode);
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    protected void hydrateHeightAndWidth(U uploaded, BufferedImage image) throws IOException {
        uploaded.setHeight(image.getHeight());
        uploaded.setWidth(image.getWidth());
    }

    public void createResized(U uploaded, BufferedImage image, String orgPath, int targetHeight, int targetWidth,
            String postfix) throws IOException {
        createResized(uploaded, image, orgPath, targetHeight, targetWidth, postfix, Scalr.Mode.FIT_TO_WIDTH);

    }

    public void createResized(U uploaded, BufferedImage image, String orgPath, int targetHeight, int targetWidth,
            String postfix, Scalr.Mode scalrMode) throws IOException {
        String imageFormat = uploaded.getExtension();

        BufferedImage scaledImg = Scalr.resize(image, Scalr.Method.QUALITY, scalrMode, targetWidth, targetHeight,
                Scalr.OP_ANTIALIAS);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int height = scaledImg.getHeight();
        int width = scaledImg.getWidth();
        ImageIO.write(scaledImg, imageFormat, baos);
        baos.flush();
        byte[] scaledImageInByte = baos.toByteArray();
        baos.close();

        String relativePath = FilenameUtils.removeExtension(uploaded.getCloudKey());
        if (!"org".equals(postfix)) {
            relativePath = relativePath + "." + postfix;
        }
        relativePath = relativePath + "." + uploaded.getExtension();
        String thumbnailPath = this.uploadsFolder + relativePath;
        Log.info("Write all byptes to {0}", thumbnailPath);
        FileUtils.writeAllBytes(scaledImageInByte, new File(thumbnailPath));
        Long sizeBytes = new File(thumbnailPath).length();
        //String url = "{cdnUrl}/st-publisher/files/view/" + uploaded.getSecret() + "/" + uploaded.getId() + "/" + postfix + "?ts=" + DateUtils.mils();
        String url = makeRawUrlForFile(uploaded, postfix);
        if (postfix.equals("thumb")) {
            uploaded.setThumbCloudKey(relativePath);
            uploaded.setThumbRawUrl(url);
            uploaded.setThumbHeight(height);
            uploaded.setThumbWidth(width);
        } else if (postfix.equals("small")) {
            uploaded.setSmallCloudKey(relativePath);
            uploaded.setSmallRawUrl(url);
            uploaded.setSmallHeight(height);
            uploaded.setSmallWidth(width);
        } else if (postfix.equals("medium")) {
            uploaded.setMediumCloudKey(relativePath);
            uploaded.setMediumRawUrl(url);
            uploaded.setMediumHeight(height);
            uploaded.setMediumWidth(width);
        } else if (postfix.equals("org")) {
            uploaded.setCloudKey(relativePath);
            uploaded.setRawUrl(url);
            uploaded.setSizeBytes(sizeBytes);
            uploaded.setHeight(height);
            uploaded.setWidth(width);
        }

        //return scaledImageInByte;
    }

    private String getFileNameFromPart(final Part part) {
        final String partHeader = part.getHeader("content-disposition");
        Log.info("Part Header = {0}", partHeader);
        for (String content : part.getHeader("content-disposition").split(";")) {
            if (content.trim().startsWith("filename")) {
                return content.substring(content.indexOf('=') + 1).trim().replace("\"", "");
            }
        }
        return null;
    }

}