All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.cloudinary.Cloudinary Maven / Gradle / Ivy

Go to download

Cloudinary is a cloud service that offers a solution to a web application's entire image management pipeline. Upload images to the cloud. Automatically perform smart image resizing, cropping and conversion without installing any complex software. Integrate Facebook or Twitter profile image extraction in a snap, in any dimension and style to match your website’s graphics requirements. Images are seamlessly delivered through a fast CDN, and much much more. This Java library allows to easily integrate with Cloudinary in Java applications.

There is a newer version: 1.0.1-dummy
Show newest version
package com.cloudinary;

import com.cloudinary.api.signing.ApiResponseSignatureVerifier;
import com.cloudinary.api.signing.NotificationRequestSignatureVerifier;
import com.cloudinary.strategies.AbstractApiStrategy;
import com.cloudinary.strategies.AbstractUploaderStrategy;
import com.cloudinary.strategies.StrategyLoader;
import com.cloudinary.utils.Analytics;
import com.cloudinary.utils.ObjectUtils;
import com.cloudinary.utils.StringUtils;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.SecureRandom;
import java.util.*;

import static com.cloudinary.Util.buildMultiParams;

@SuppressWarnings({"rawtypes", "unchecked"})
public class Cloudinary {

    private static List UPLOAD_STRATEGIES = new ArrayList(Arrays.asList(
            "com.cloudinary.android.UploaderStrategy",
            "com.cloudinary.http42.UploaderStrategy",
            "com.cloudinary.http43.UploaderStrategy",
            "com.cloudinary.http44.UploaderStrategy",
            "com.cloudinary.http45.UploaderStrategy"));
    public static List API_STRATEGIES = new ArrayList(Arrays.asList(
            "com.cloudinary.android.ApiStrategy",
            "com.cloudinary.http42.ApiStrategy",
            "com.cloudinary.http43.ApiStrategy",
            "com.cloudinary.http44.ApiStrategy",
            "com.cloudinary.http45.ApiStrategy"));

    public final static String CF_SHARED_CDN = "d3jpl91pxevbkh.cloudfront.net";
    public final static String OLD_AKAMAI_SHARED_CDN = "cloudinary-a.akamaihd.net";
    public final static String AKAMAI_SHARED_CDN = "res.cloudinary.com";
    public final static String SHARED_CDN = AKAMAI_SHARED_CDN;

    public final static String VERSION = "1.0.0-dummy";
    static String USER_AGENT_PREFIX = "CloudinaryJava";
    public final static String USER_AGENT_JAVA_VERSION = "(Java " + System.getProperty("java.version") + ")";

    public final Configuration config;
    private AbstractUploaderStrategy uploaderStrategy;
    private AbstractApiStrategy apiStrategy;
    private String userAgent = USER_AGENT_PREFIX+"/"+ VERSION + " "+USER_AGENT_JAVA_VERSION;
    public Analytics analytics = new Analytics();
    public Uploader uploader() {
        return new Uploader(this, uploaderStrategy);
    }

    public Api api() {
        return new Api(this, apiStrategy);
    }

    public Search search() {
        return new Search(this);
    }

    public static void registerUploaderStrategy(String className) {
        if (!UPLOAD_STRATEGIES.contains(className)) {
            UPLOAD_STRATEGIES.add(className);
        }

    }

    public static void registerAPIStrategy(String className) {
        if (!API_STRATEGIES.contains(className)) {
            API_STRATEGIES.add(className);
        }
    }

    private void loadStrategies() {
        if (!this.config.loadStrategies) return;
        uploaderStrategy = StrategyLoader.find(UPLOAD_STRATEGIES);

        if (uploaderStrategy == null) {
            throw new UnknownError("Can't find Cloudinary platform adapter [" + StringUtils.join(UPLOAD_STRATEGIES, ",") + "]");
        }

        apiStrategy = StrategyLoader.find(API_STRATEGIES);
        if (apiStrategy == null) {
            throw new UnknownError("Can't find Cloudinary platform adapter [" + StringUtils.join(API_STRATEGIES, ",") + "]");
        }
    }

    public Cloudinary(Map config) {
        this.config = new Configuration(config);
        loadStrategies();
    }

    public Cloudinary(String cloudinaryUrl) {
        this.config = Configuration.from(cloudinaryUrl);
        loadStrategies();
    }

    public Cloudinary() {
        String cloudinaryUrl = System.getProperty("CLOUDINARY_URL", System.getenv("CLOUDINARY_URL"));
        if (cloudinaryUrl != null) {
            this.config = Configuration.from(cloudinaryUrl);
        } else {
            this.config = new Configuration();
        }
        loadStrategies();
    }

    public Url url() {
        return new Url(this);
    }

    public String cloudinaryApiUrl(String action, Map options) {
        String cloudinary = ObjectUtils.asString(options.get("upload_prefix"),
                ObjectUtils.asString(this.config.uploadPrefix, "https://api.cloudinary.com"));
        String cloud_name = ObjectUtils.asString(options.get("cloud_name"), ObjectUtils.asString(this.config.cloudName));
        if (cloud_name == null)
            throw new IllegalArgumentException("Must supply cloud_name in tag or in configuration");
        String resource_type = ObjectUtils.asString(options.get("resource_type"), "image");
        return StringUtils.join(new String[]{cloudinary, "v1_1", cloud_name, resource_type, action}, "/");
    }

    private final static SecureRandom RND = new SecureRandom();

    public String randomPublicId() {
        byte[] bytes = new byte[8];
        RND.nextBytes(bytes);
        return StringUtils.encodeHexString(bytes);
    }

    public String signedPreloadedImage(Map result) {
        return result.get("resource_type") + "/upload/v" + result.get("version") + "/" + result.get("public_id")
                + (result.containsKey("format") ? "." + result.get("format") : "") + "#" + result.get("signature");
    }

    public String apiSignRequest(Map paramsToSign, String apiSecret) {
        return Util.produceSignature(paramsToSign, apiSecret, config.signatureAlgorithm);
    }

    /**
     * @return the userAgent that will be sent with every API call.
     */
    public String getUserAgent(){
        return userAgent;
    }

    /**
     * Set the prefix and version for the user agent that will be sent with every API call
     * a userAgent is built from `prefix/version (additional data)`
     * @param prefix - the prefix of the userAgent to be set
     * @param version - the version of the userAgent to be set
     */
    public void setUserAgent(String prefix, String version){
        userAgent = prefix+"/"+ version + " ("+USER_AGENT_PREFIX+ " "+VERSION+") " + USER_AGENT_JAVA_VERSION;
    }

    /**
     * Set the analytics object that will be sent with every URL generation call.
     * @param analytics - the analytics object to set
     */
    public void setAnalytics(Analytics analytics) {
        this.analytics = analytics;
    }

    /**
     * Verifies that Cloudinary notification request is genuine by checking its signature.
     *
     * Cloudinary can asynchronously process your e.g. image uploads requests. This is achieved by calling back API you
     * specified during preparing of upload request as soon as it has been processed. See Upload Notifications in
     * Cloudinary documentation for more details. In order to make sure it is Cloudinary calling your API back, hashed
     * message authentication codes (HMAC's) based on agreed hashing function and configured Cloudinary API secret key
     * are used for signing the requests.
     *
     * The following method serves as a convenient utility to perform the verification procedure.
     *
     * @param body Cloudinary Notification request body represented as string
     * @param timestamp Cloudinary Notification request custom X-Cld-Timestamp HTTP header value
     * @param signature Cloudinary Notification request custom X-Cld-Signature HTTP header value, i.e. the HMAC
     * @param validFor desired period of request validity since issued, in seconds, for protection against replay attacks
     * @return whether request signature is valid or not
     */
    public boolean verifyNotificationSignature(String body, String timestamp, String signature, long validFor) {
        return new NotificationRequestSignatureVerifier(config.apiSecret, config.signatureAlgorithm).verifySignature(body, timestamp, signature, validFor);
    }

    /**
     * Verifies that Cloudinary API response is genuine by checking its signature.
     *
     * Cloudinary can add a signature value in the response to API methods returning public id's and versions. In order
     * to make sure it is genuine Cloudinary response, hashed message authentication codes (HMAC's) based on agreed hashing
     * function and configured Cloudinary API secret key are used for signing the responses.
     *
     * The following method serves as a convenient utility to perform the verification procedure.
     *
     * @param publicId publicId response field value
     * @param version version response field value
     * @param signature signature response field value, i.e. the HMAC
     * @return whether response signature is valid or not
     */
    public boolean verifyApiResponseSignature(String publicId, String version, String signature) {
        return new ApiResponseSignatureVerifier(config.apiSecret, config.signatureAlgorithm).verifySignature(publicId, version, signature);
    }

    public void signRequest(Map params, Map options) {
        String apiKey = ObjectUtils.asString(options.get("api_key"), this.config.apiKey);
        if (apiKey == null)
            throw new IllegalArgumentException("Must supply api_key");
        String apiSecret = ObjectUtils.asString(options.get("api_secret"), this.config.apiSecret);
        if (apiSecret == null)
            throw new IllegalArgumentException("Must supply api_secret");
        Util.clearEmpty(params);
        params.put("signature", this.apiSignRequest(params, apiSecret));
        params.put("api_key", apiKey);
    }

    public String privateDownload(String publicId, String format, Map options) throws Exception {
        Map params = new HashMap();
        params.put("public_id", publicId);
        params.put("format", format);
        params.put("attachment", options.get("attachment"));
        params.put("type", options.get("type"));
        params.put("expires_at", options.get("expires_at"));
        params.put("timestamp", Util.timestamp());
        signRequest(params, options);
        return buildUrl(cloudinaryApiUrl("download", options), params);
    }

    public String zipDownload(String tag, Map options) throws Exception {
        Map params = new HashMap();
        params.put("timestamp", Util.timestamp());
        params.put("tag", tag);
        Object transformation = options.get("transformation");
        if (transformation != null) {
            if (transformation instanceof Transformation) {
                transformation = ((Transformation) transformation).generate();
            }
            params.put("transformation", transformation.toString());
        }
        params.put("transformation", transformation);
        signRequest(params, options);
        return buildUrl(cloudinaryApiUrl("download_tag.zip", options), params);
    }

    public String downloadArchive(Map options, String targetFormat) throws UnsupportedEncodingException {
        Map params = Util.buildArchiveParams(options, targetFormat);
        params.put("mode", ArchiveParams.MODE_DOWNLOAD);
        signRequest(params, options);
        return buildUrl(cloudinaryApiUrl("generate_archive", options), params);
    }

    public String downloadArchive(ArchiveParams params) throws UnsupportedEncodingException {
        return downloadArchive(params.toMap(), params.targetFormat());
    }

    public String downloadZip(Map options) throws UnsupportedEncodingException {
        return downloadArchive(options, "zip");
    }

    public String downloadGeneratedSprite(String tag, Map options) throws IOException {
        if (StringUtils.isEmpty(tag)) throw new IllegalArgumentException("Tag cannot be empty");

        if (options == null)
            options = new HashMap();

        options.put("tag", tag);
        options.put("mode", ArchiveParams.MODE_DOWNLOAD);

        Map params = Util.buildGenerateSpriteParams(options);
        signRequest(params, options);

        return buildUrl(cloudinaryApiUrl("sprite", options), params);
    }

    public String downloadGeneratedSprite(String[] urls, Map options) throws IOException {
        if (urls.length < 1) throw new IllegalArgumentException("Request must contain at least one URL.");
        if (options == null)
            options = new HashMap();

        options.put("urls", urls);
        options.put("mode", ArchiveParams.MODE_DOWNLOAD);

        Map params = Util.buildGenerateSpriteParams(options);
        signRequest(params, options);

        return buildUrl(cloudinaryApiUrl("sprite", options), params);
    }

    public String downloadMulti(String tag, Map options) throws IOException {
        if (StringUtils.isEmpty(tag)) throw new IllegalArgumentException("Tag cannot be empty");
        if (options == null)
            options = new HashMap();

        options.put("tag", tag);
        options.put("mode", ArchiveParams.MODE_DOWNLOAD);

        Map params = buildMultiParams(options);
        signRequest(params, options);

        return buildUrl(cloudinaryApiUrl("multi", options), params);
    }

    public String downloadMulti(String[] urls, Map options) throws IOException {
        if (urls.length < 1) throw new IllegalArgumentException("Request must contain at least one URL.");
        if (options == null)
            options = new HashMap();

        options.put("urls", urls);
        options.put("mode", ArchiveParams.MODE_DOWNLOAD);

        Map params = buildMultiParams(options);
        signRequest(params, options);

        return buildUrl(cloudinaryApiUrl("multi", options), params);
    }

    /**
     * Generates URL for executing "Download Folder" operation on Cloudinary site.
     * 
     * @param folderPath path of folder to generate download URL for
     * @param options    optional, holds hints for URL generation procedure, see documentation for full list
     * @return generated URL for downloading specified folder as ZIP archive
     */
    public String downloadFolder(String folderPath, Map options) throws UnsupportedEncodingException {
        if (StringUtils.isEmpty(folderPath)) {
            throw new IllegalArgumentException("Folder path parameter value is required");
        }

        Map adjustedOptions = new HashMap();
        if (options != null) {
            adjustedOptions.putAll(options);
        }

        adjustedOptions.put("prefixes", folderPath);

        final Object resourceType = adjustedOptions.get("resource_type");
        adjustedOptions.put("resource_type", resourceType != null ? resourceType : "all");

        return downloadArchive(adjustedOptions, (String) adjustedOptions.get("target_format"));
    }

    /**
     * Returns an URL of a specific version of a backed up asset that can be used to download that
     * version of the asset (within an hour of the request).
     *
     * @param assetId   The identifier of the uploaded asset.
     * @param versionId The identifier of a backed up version of the asset.
     * @param options   Optional, holds hints for URL generation procedure, see documentation for
     *                  full list
     * @return          The download URL of the asset
     */
    public String downloadBackedupAsset(String assetId, String versionId, Map options) throws UnsupportedEncodingException {
        if (StringUtils.isEmpty(assetId)) {
            throw new IllegalArgumentException("AssetId parameter is required");
        }

        if (StringUtils.isEmpty(versionId)) {
            throw new IllegalArgumentException("VersionId parameter is required");
        }

        Map params = new HashMap();
        params.put("asset_id", assetId);
        params.put("version_id", versionId);
        params.put("timestamp", Util.timestamp());

        signRequest(params, options);
        return buildUrl(cloudinaryApiUrl("download_backup", options), params);
    }

    private String buildUrl(String base, Map params) throws UnsupportedEncodingException {
        StringBuilder urlBuilder = new StringBuilder();
        urlBuilder.append(base);
        if (!params.isEmpty()) {
            urlBuilder.append("?");
        }
        boolean first = true;
        for (Map.Entry param : params.entrySet()) {
            if (param.getValue() == null) continue;

            String keyValue = null;
            Object value = param.getValue();
            if (!first) urlBuilder.append("&");
            if (value instanceof Object[])
                value = Arrays.asList(value);
            if (value instanceof Collection) {
                String key = param.getKey() + "[]=";
                Collection items = (Collection) value;
                List encodedItems = new ArrayList();
                for (Object item : items)
                    encodedItems.add(URLEncoder.encode(item.toString(), "UTF-8"));
                keyValue = key + StringUtils.join(encodedItems, "&" + key);
            } else {
                keyValue = param.getKey() + "=" +
                        URLEncoder.encode(value.toString(), "UTF-8");
            }
            urlBuilder.append(keyValue);
            first = false;
        }
        return urlBuilder.toString();
    }

    @Deprecated
    public static Map asMap(Object... values) {
        return ObjectUtils.asMap(values);
    }
}