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

com.cloudinary.Url 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.

The newest version!
package com.cloudinary;

import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.CRC32;

import com.cloudinary.utils.Analytics;
import com.cloudinary.utils.Base64Coder;
import com.cloudinary.utils.ObjectUtils;
import com.cloudinary.utils.StringUtils;

import static com.cloudinary.SignatureAlgorithm.SHA256;

public class Url {
    private final Cloudinary cloudinary;
    private final Configuration config;
    private boolean longUrlSignature;
    String publicId = null;
    String type = null;
    String resourceType = null;
    String format = null;
    String version = null;
    Transformation transformation = null;
    boolean signUrl;
    private AuthToken authToken;
    String source = null;
    private String urlSuffix;
    private Boolean useRootPath;
    private Boolean useFetchFormat;
    Map sourceTransformation = null;
    String[] sourceTypes = null;
    String fallbackContent = null;
    Transformation posterTransformation = null;
    String posterSource = null;
    Url posterUrl = null;

    private static final String CL_BLANK = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
    public static final String[] DEFAULT_VIDEO_SOURCE_TYPES = {"webm", "mp4", "ogv"};
    private static final Pattern VIDEO_EXTENSION_RE = Pattern.compile("\\.(" + StringUtils.join(DEFAULT_VIDEO_SOURCE_TYPES, "|") + ")$");

    public Url(Cloudinary cloudinary) {
        this.cloudinary = cloudinary;
        this.config = new Configuration(cloudinary.config);
        this.longUrlSignature = config.longUrlSignature;
        this.authToken = config.authToken;
    }

    public Url clone() {
        Url cloned = cloudinary.url();
        cloned.config.update(config.asMap());
        cloned.fallbackContent = this.fallbackContent;
        cloned.format = this.format;
        cloned.posterSource = this.posterSource;
        if (this.posterTransformation != null)
            cloned.posterTransformation = new Transformation(this.posterTransformation);
        if (this.posterUrl != null) cloned.posterUrl = this.posterUrl.clone();
        cloned.publicId = this.publicId;
        cloned.resourceType = this.resourceType;
        cloned.signUrl = this.signUrl;
        cloned.source = this.source;
        if (this.transformation != null) cloned.transformation = new Transformation(this.transformation);
        if (this.sourceTransformation != null) {
            cloned.sourceTransformation = new HashMap();
            for (Map.Entry keyValuePair : this.sourceTransformation.entrySet()) {
                cloned.sourceTransformation.put(keyValuePair.getKey(), keyValuePair.getValue());
            }
        }
        cloned.sourceTypes = this.sourceTypes;
        cloned.urlSuffix = this.urlSuffix;
        cloned.useRootPath = this.useRootPath;
        cloned.useFetchFormat = this.useFetchFormat;
        cloned.longUrlSignature = this.longUrlSignature;
        cloned.authToken = this.authToken;
        return cloned;
    }

    private static Pattern identifierPattern = Pattern.compile("^(?:([^/]+)/)??(?:([^/]+)/)??(?:v(\\d+)/)?" + "(?:([^#/]+?)(?:\\.([^.#/]+))?)(?:#([^/]+))?$");

    /**
     * Parses a cloudinary identifier of the form:
* {@code [/][/][v/][.][#]} */ public Url fromIdentifier(String identifier) { Matcher matcher = identifierPattern.matcher(identifier); if (!matcher.matches()) { throw new RuntimeException(String.format("Couldn't parse identifier %s", identifier)); } String resourceType = matcher.group(1); if (resourceType != null) { resourceType(resourceType); } String type = matcher.group(2); if (type != null) { type(type); } String version = matcher.group(3); if (version != null) { version(version); } String publicId = matcher.group(4); if (publicId != null) { publicId(publicId); } String format = matcher.group(5); if (format != null) { format(format); } // Signature (group 6) is not used return this; } public Url type(String type) { this.type = type; return this; } public Url resourcType(String resourceType) { return resourceType(resourceType); } public Url resourceType(String resourceType) { this.resourceType = resourceType; return this; } public Url publicId(Object publicId) { this.publicId = ObjectUtils.asString(publicId); return this; } public Url format(String format) { this.format = format; return this; } public Url cloudName(String cloudName) { this.config.cloudName = cloudName; return this; } public Url secureDistribution(String secureDistribution) { this.config.secureDistribution = secureDistribution; return this; } public Url secureCdnSubdomain(boolean secureCdnSubdomain) { this.config.secureCdnSubdomain = secureCdnSubdomain; return this; } public Url suffix(String urlSuffix) { this.urlSuffix = urlSuffix; return this; } public Url useRootPath(boolean useRootPath) { this.useRootPath = useRootPath; return this; } public Url useFetchFormat(boolean useFetchFormat) { this.useFetchFormat = useFetchFormat; return this; } public Url cname(String cname) { this.config.cname = cname; return this; } public Url version(Object version) { this.version = ObjectUtils.asString(version); return this; } public Url transformation(Transformation transformation) { this.transformation = transformation; return this; } public Url secure(boolean secure) { this.config.secure = secure; return this; } public Url privateCdn(boolean privateCdn) { this.config.privateCdn = privateCdn; return this; } public Url cdnSubdomain(boolean cdnSubdomain) { this.config.cdnSubdomain = cdnSubdomain; return this; } public Url shorten(boolean shorten) { this.config.shorten = shorten; return this; } public Transformation transformation() { if (this.transformation == null) this.transformation = new Transformation(); return this.transformation; } public Url signed(boolean signUrl) { this.signUrl = signUrl; return this; } /** * Set the authorization token. If authToken has already been set the parameter is merged with the current value unless the parameter value is null or NULL_AUTH_TOKEN.

* For example, to generate an authorized URL with a different duration:
*
     *  {@code
     *   cloudinary.config.authToken = new AuthToken(KEY).duration(500);
     *   // later...
     *   cloudinary.url().signed(true).authToken(new AuthToken().duration(300))
     *                   .type("authenticated").version("1486020273").generate("sample.jpg");
     *  }
     * 
* * @param authToken an authorization token object * @return this */ public Url authToken(AuthToken authToken) { if (this.authToken == null) { this.authToken = authToken; } else if (authToken == null || authToken.equals(AuthToken.NULL_AUTH_TOKEN)) { this.authToken = authToken; } else { this.authToken = this.authToken.merge(authToken); } return this; } public Url longUrlSignature(boolean isLong) { this.longUrlSignature = isLong; return this; } public Url sourceTransformation(Map sourceTransformation) { this.sourceTransformation = sourceTransformation; return this; } public Url sourceTransformationFor(String source, Transformation transformation) { if (this.sourceTransformation == null) { this.sourceTransformation = new HashMap(); } this.sourceTransformation.put(source, transformation); return this; } public Url sourceTypes(String[] sourceTypes) { this.sourceTypes = sourceTypes; return this; } public Url fallbackContent(String fallbackContent) { this.fallbackContent = fallbackContent; return this; } public Url posterTransformation(Transformation posterTransformation) { this.posterTransformation = posterTransformation; return this; } @SuppressWarnings("rawtypes") public Url posterTransformation(List posterTransformations) { this.posterTransformation = new Transformation(posterTransformations); return this; } @SuppressWarnings({"rawtypes", "unchecked"}) public Url posterTransformation(Map posterTransformations) { List transformations = new ArrayList(); Map copy = new HashMap(); copy.putAll(posterTransformations); transformations.add(copy); this.posterTransformation = new Transformation(transformations); return this; } public Url posterSource(String posterSource) { this.posterSource = posterSource; return this; } public Url posterUrl(Url posterUrl) { this.posterUrl = posterUrl; return this; } public Url poster(Object poster) { if (poster instanceof Transformation) { return posterTransformation((Transformation) poster); } else if (poster instanceof List) { return posterTransformation((List) poster); } else if (poster instanceof Map) { return posterTransformation((Map) poster); } else if (poster instanceof Url) { return posterUrl((Url) poster); } else if (poster instanceof String) { return posterSource((String) poster); } else if (poster == null || poster.equals(Boolean.FALSE)) { return posterSource(""); } else { throw new IllegalArgumentException("Illegal value type supplied to poster. must be one of: , >, , , "); } } /** * Indicates whether to add '/v1/' to the URL when the public ID includes folders and a 'version' value was * not defined. * When no version is explicitly specified and the public id contains folders, a default v1 version * is added to the url. This boolean can disable that behaviour. * @param forceVersion Whether to add the version to the url. * @return This same Url instance for chaining. */ public Url forceVersion(boolean forceVersion){ this.config.forceVersion = forceVersion; return this; } public String generate() { return generate(null); } public String generate(String source) { boolean useRootPath = this.config.useRootPath; if (this.useRootPath != null) { useRootPath = this.useRootPath; } if (StringUtils.isEmpty(this.config.cloudName)) { throw new IllegalArgumentException("Must supply cloud_name in tag or in configuration"); } if (source == null) { if (publicId == null) { if (this.source == null) { return null; } source = this.source; } else { source = publicId; } } boolean httpSource = StringUtils.isHttpUrl(source); if (httpSource) { if (StringUtils.isEmpty(type) || "asset".equals(type)) { return source; } } if ((type != null && type.equals("fetch") || (useFetchFormat != null && useFetchFormat)) && !StringUtils.isEmpty(format)) { transformation().fetchFormat(format); this.format = null; } String transformationStr = transformation().generate(); String signature = ""; String[] finalizedSource = finalizeSource(source, format, urlSuffix); source = finalizedSource[0]; String sourceToSign = finalizedSource[1]; if (this.config.forceVersion && sourceToSign.contains("/") && !StringUtils.startWithVersionString(sourceToSign) && !httpSource && StringUtils.isEmpty(version)) { version = "1"; } if (version == null) version = ""; else version = "v" + version; if (signUrl && (authToken == null || authToken.equals(AuthToken.NULL_AUTH_TOKEN))) { SignatureAlgorithm signatureAlgorithm = longUrlSignature ? SHA256 : config.signatureAlgorithm; String toSign = StringUtils.join(new String[]{transformationStr, sourceToSign}, "/"); toSign = StringUtils.removeStartingChars(toSign, '/'); toSign = StringUtils.mergeSlashesInUrl(toSign); byte[] hash = Util.hash(toSign + this.config.apiSecret, signatureAlgorithm); signature = Base64Coder.encodeURLSafeString(hash); signature = "s--" + signature.substring(0, longUrlSignature ? 32 : 8) + "--"; } String resourceType = this.resourceType; if (resourceType == null) resourceType = "image"; String finalResourceType = finalizeResourceType(resourceType, type, urlSuffix, useRootPath, config.shorten); String prefix = unsignedDownloadUrlPrefix(source, config); String join = StringUtils.join(new String[]{prefix, finalResourceType, signature, transformationStr, version, source}, "/"); String url = StringUtils.mergeSlashesInUrl(join); if (signUrl && authToken != null && !authToken.equals(AuthToken.NULL_AUTH_TOKEN)) { try { URL tempUrl = new URL(url); String path = tempUrl.getPath(); String token = authToken.generate(path); url = url + "?" + token; } catch (MalformedURLException ignored) { } } if (cloudinary.config.analytics != null && cloudinary.config.analytics) { try { URL tempUrl = new URL(url); // if any other query param already exist on the URL do not add analytics query param. if (tempUrl.getQuery() == null) { String path = tempUrl.getPath(); url = url + "?" + cloudinary.analytics.toQueryParam(); } } catch (MalformedURLException ignored) { } } return url; } private String[] finalizeSource(String source, String format, String urlSuffix) { source = StringUtils.mergeSlashesInUrl(source); String[] result = new String[2]; String sourceToSign; if (StringUtils.isHttpUrl(source)) { source = SmartUrlEncoder.encode(source); sourceToSign = source; } else { try { source = SmartUrlEncoder.encode(URLDecoder.decode(source.replace("+", "%2B"), "UTF-8")); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } sourceToSign = source; if (StringUtils.isNotBlank(urlSuffix)) { if (urlSuffix.contains(".") || urlSuffix.contains("/")) { throw new IllegalArgumentException("url_suffix should not include . or /"); } source = source + "/" + urlSuffix; } if (StringUtils.isNotBlank(format)) { source = source + "." + format; sourceToSign = sourceToSign + "." + format; } } result[0] = source; result[1] = sourceToSign; return result; } public String finalizeResourceType(String resourceType, String type, String urlSuffix, boolean useRootPath, boolean shorten) { if (type == null) { type = "upload"; } if (!StringUtils.isBlank(urlSuffix)) { if (resourceType.equals("image") && type.equals("upload")) { resourceType = "images"; type = null; } else if (resourceType.equals("image") && type.equals("private")) { resourceType = "private_images"; type = null; } else if (resourceType.equals("image") && type.equals("authenticated")) { resourceType = "authenticated_images"; type = null; } else if (resourceType.equals("raw") && type.equals("upload")) { resourceType = "files"; type = null; } else if (resourceType.equals("video") && type.equals("upload")) { resourceType = "videos"; type = null; } else { throw new IllegalArgumentException("URL Suffix only supported for image/upload, image/private, raw/upload, image/authenticated and video/upload"); } } if (useRootPath) { if ((resourceType.equals("image") && type.equals("upload")) || (resourceType.equals("images") && StringUtils.isBlank(type))) { resourceType = null; type = null; } else { throw new IllegalArgumentException("Root path only supported for image/upload"); } } if (shorten && resourceType.equals("image") && type.equals("upload")) { resourceType = "iu"; type = null; } String result = resourceType; if (type != null) { result += "/" + type; } return result; } public static String unsignedDownloadUrlPrefix(String source, Configuration config) { if (config.cloudName.startsWith("/")) { return "/res" + config.cloudName; } boolean sharedDomain = !config.privateCdn; String prefix; String cloudName; String secureDistribution = config.secureDistribution; Boolean secureCdnSubdomain = null; if (config.secure) { if (StringUtils.isEmpty(config.secureDistribution) || config.secureDistribution.equals(Cloudinary.OLD_AKAMAI_SHARED_CDN)) { secureDistribution = config.privateCdn ? config.cloudName + "-res.cloudinary.com" : Cloudinary.SHARED_CDN; } if (!sharedDomain) { sharedDomain = secureDistribution.equals(Cloudinary.SHARED_CDN); } if (secureCdnSubdomain == null && sharedDomain) { secureCdnSubdomain = config.cdnSubdomain; } if (secureCdnSubdomain != null && secureCdnSubdomain == true) { secureDistribution = config.secureDistribution.replace("res.cloudinary.com", "res-" + shard(source) + ".cloudinary.com"); } prefix = "https://" + secureDistribution; } else if (StringUtils.isNotBlank(config.cname)) { String subdomain = config.cdnSubdomain ? "a" + shard(source) + "." : ""; prefix = "http://" + subdomain + config.cname; } else { String protocol = "http://"; cloudName = config.privateCdn ? config.cloudName + "-" : ""; String res = "res"; String subdomain = config.cdnSubdomain ? "-" + shard(source) : ""; String domain = ".cloudinary.com"; prefix = StringUtils.join(new String[]{protocol, cloudName, res, subdomain, domain}, ""); } if (sharedDomain) { prefix += "/" + config.cloudName; } return prefix; } private static String shard(String input) { CRC32 crc32 = new CRC32(); crc32.update(Util.getUTF8Bytes(input)); return String.valueOf((crc32.getValue() % 5 + 5) % 5 + 1); } @SuppressWarnings("unchecked") public String imageTag(String source) { return imageTag(source, ObjectUtils.emptyMap()); } public String imageTag(Map attributes) { return imageTag(null, attributes); } public String imageTag(String source, Map attributes) { String url = generate(source); attributes = new TreeMap(attributes); // Make sure they // are ordered. if (transformation().getHtmlHeight() != null) attributes.put("height", transformation().getHtmlHeight()); if (transformation().getHtmlWidth() != null) attributes.put("width", transformation().getHtmlWidth()); boolean hiDPI = transformation().isHiDPI(); boolean responsive = transformation().isResponsive(); if (!config.clientHints && (hiDPI || responsive)) { attributes.put("data-src", url); String extraClass = responsive ? "cld-responsive" : "cld-hidpi"; attributes.put("class", (StringUtils.isBlank(attributes.get("class")) ? "" : attributes.get("class") + " ") + extraClass); String responsivePlaceholder = attributes.remove("responsive_placeholder"); if ("blank".equals(responsivePlaceholder)) { responsivePlaceholder = CL_BLANK; } url = responsivePlaceholder; } StringBuilder builder = new StringBuilder(); builder.append(" attr : attributes.entrySet()) { builder.append(" ").append(attr.getKey()).append("='").append(attr.getValue()).append("'"); } builder.append("/>"); return builder.toString(); } public String videoTag() { return videoTag("", new HashMap()); } public String videoTag(String source) { return videoTag(source, new HashMap()); } public String videoTag(Map attributes) { return videoTag("", attributes); } private String finalizePosterUrl(String source) { String posterUrl = null; if (this.posterUrl != null) { posterUrl = this.posterUrl.generate(); } else if (this.posterTransformation != null) { posterUrl = this.clone().format("jpg").transformation(new Transformation(this.posterTransformation)) .generate(source); } else if (this.posterSource != null) { if (!StringUtils.isEmpty(this.posterSource)) posterUrl = this.clone().format("jpg").generate(this.posterSource); } else { posterUrl = this.clone().format("jpg").generate(source); } return posterUrl; } private void appendVideoSources(StringBuilder html, String source, String sourceType) { Url sourceUrl = this.clone(); if (this.sourceTransformation != null) { Transformation transformation = this.transformation; Transformation sourceTransformation = null; if (this.sourceTransformation.get(sourceType) != null) sourceTransformation = new Transformation(this.sourceTransformation.get(sourceType)); if (transformation == null) { transformation = sourceTransformation; } else if (sourceTransformation != null) { transformation = transformation.chainWith(sourceTransformation); } sourceUrl.transformation(transformation); } String src = sourceUrl.format(sourceType).generate(source); String videoType = sourceType; if (sourceType.equals("ogv")) videoType = "ogg"; String mimeType = "video/" + videoType; html.append(""); } public String videoTag(String source, Map attributes) { if (StringUtils.isEmpty(source)) source = this.source; if (StringUtils.isEmpty(source)) source = publicId; if (StringUtils.isEmpty(source)) throw new IllegalArgumentException("must supply source or public id"); source = VIDEO_EXTENSION_RE.matcher(source).replaceFirst(""); if (this.resourceType == null) this.resourceType = "video"; attributes = new TreeMap(attributes); // Make sure they are ordered. String[] sourceTypes = this.sourceTypes; if (sourceTypes == null) { sourceTypes = DEFAULT_VIDEO_SOURCE_TYPES; } String posterUrl = this.finalizePosterUrl(source); if (!StringUtils.isEmpty(posterUrl)) attributes.put("poster", posterUrl); StringBuilder html = new StringBuilder().append(" 1; if (!multiSource) { url = generate(source + "." + sourceTypes[0]); attributes.put("src", url); } else { generate(source); } if (this.transformation.getHtmlHeight() != null) attributes.put("height", this.transformation.getHtmlHeight()); if (attributes.containsKey("html_height")) attributes.put("height", attributes.remove("html_height")); if (this.transformation.getHtmlWidth() != null) attributes.put("width", this.transformation.getHtmlWidth()); if (attributes.containsKey("html_width")) attributes.put("width", attributes.remove("html_width")); for (Map.Entry attr : attributes.entrySet()) { html.append(" ").append(attr.getKey()); if (attr.getValue() != null) { String value = ObjectUtils.asString(attr.getValue()); html.append("='").append(value).append("'"); } } html.append(">"); if (multiSource) { for (String sourceType : sourceTypes) { this.appendVideoSources(html, source, sourceType); } } if (this.fallbackContent != null) html.append(this.fallbackContent); html.append(""); return html.toString(); } public String generateSpriteCss(String source) { this.type = "sprite"; if (!source.endsWith(".css")) this.format = "css"; return generate(source); } public Url source(String source) { this.source = source; return this; } public Url source(StoredFile source) { if (source.getResourceType() != null) this.resourceType = source.getResourceType(); if (source.getType() != null) this.type = source.getType(); if (source.getVersion() != null) this.version = source.getVersion().toString(); this.format = source.getFormat(); this.source = source.getPublicId(); return this; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy