FeedBurner makes it easy to receive content updates in My Yahoo!, Newsgator, Bloglines, and other news readers.
Learn more about syndication and FeedBurner...
Controlling who can access your images and videos, and when, can be an important concern for your business and security workflow. You may have resources that you only want some of your users or employees to access, or you may need to make sure that your original resources are secure, and only transformed (edited) versions of your resources are delivered, e.g., with a watermark or logo displayed.
Cloudinary prides itself on the wide range of manipulations available via dynamic URLs, and there are huge benefits to using on-the-fly dynamic manipulations to deliver your public resources. But this flexibility for delivering resources may be too permissive in certain situations because simply changing or removing a dynamic Cloudinary URL parameter can deliver a new image on-the-fly. This can result in users tweaking a URL in order to access the original version of images, for example to view an image without the added watermark, minus the cropping parameters that have isolated a specific face in the image, or without the pixelate_faces
effect hiding people's identity, etc.
To provide more control over access to your images, Cloudinary offers out-of-the-box support for uploading resources as authenticated. By default, these authenticated resources can only be accessed with a signed delivery URL: a dynamic URL with a signature that must be validated before the resource is delivered. The signature is based on all the dynamic parameters included in the URL, so changing any parameter or value in the URL will invalidate the signature and thus access to the requested resource will be denied.
An example of the single line of code needed for uploading an image as authenticated:
Cloudinary::Uploader.upload("bag.jpg", :type => :authenticated)
\Cloudinary\Uploader::upload("bag.jpg", array("type" => "authenticated"));
cloudinary.uploader.upload("bag.jpg", type = "authenticated")
cloudinary.v2.uploader.upload("bag.jpg", { type: "authenticated" }, function(error, result) {console.log(result); });
cloudinary.uploader().upload("bag.jpg", ObjectUtils.asMap("type", "authenticated"));
An example of the single line of code needed to deliver an authenticated image (also scaled to a width of 300 pixels). Note the signature component in the resulting URL (automatically added when using our SDKs):
cl_image_tag("bag.jpg", :width=>300, :crop=>"scale", :sign_url=>true, :type=>"authenticated")
cl_image_tag("bag.jpg", array("width"=>300, "crop"=>"scale", "sign_url"=>true, "type"=>"authenticated"))
CloudinaryImage("bag.jpg").image(width=300, crop="scale", sign_url=True, type="authenticated")
cloudinary.image("bag.jpg", {width: 300, crop: "scale", sign_url: true, type: "authenticated"})
cloudinary.url().transformation(new Transformation().width(300).crop("scale")).signed(true).type("authenticated").imageTag("bag.jpg")
$.cloudinary.image("bag.jpg", {width: 300, crop: "scale", type: "authenticated"})
cloudinary.Api.UrlImgUp.Transform(new Transformation().Width(300).Crop("scale")).Signed(true).Type("authenticated").BuildImageTag("bag.jpg")
The signed delivery method described above may meet your authentication needs in many cases, but what if you want to limit access to your resources to a particular IP address, or to specific users, or for a limited time until they are released? In order to support more advanced requirements for authentication, Cloudinary has added new authentication functionality:
Token-based
authentication, providing IP restrictions and time-limited URLs.Cookie-based
authentication, restricting access to users with a valid cookie.When you need to control which devices have access to an image, the basic signed URL solution may not be enough.
Suppose you have images of products that have not been released yet, or user uploaded images that should not be publicly available (e.g., signed contracts). In this case, you would want to allow access only for your employees, or in effect, restrict requests to only those that have been initiated from within your network (a static IP address). That’s where token-based authentication comes in.
An authentication token is added as a set of query parameters to the image delivery URL, and is used for validation before delivering the image. Generating the token-based authentication URL is as simple as adding the sign_url
parameter (set to true
) and the auth_token
parameter in your SDK resource delivery method. The auth_token
parameter includes the details that limit access, which in our example would include values for the permitted ip
and the encryption key
you receive from Cloudinary.
For example, to deliver the "contract123" image only if the requesting IP address is 22.33.22.11:
cl_image_tag("contract123.jpg", :sign_url => true, :auth_token => { :key => "MyKey", :ip => "22.33.22.11"}, :type => "authenticated")
cl_image_tag("contract123.jpg", array("auth_token" => array( "key" => "MyKey", "ip" => "22.33.22.11"), "type" => "authenticated", "sign_url" => true));
cloudinary.CloudinaryImage("contract123.jpg").image( type = "authenticated", auth_token = dict(ip = "22.33.22.11", key = "MyKey"), sign_url = true)
cloudinary.image("contract123.jpg", { type: "authenticated", auth_token: {key: "MyKey", ip: "22.33.22.11" }, sign_url: true })
cloudinary.url().transformation(new Transformation(). type("authenticated").authToken(new AuthToken("MyKey"). ip("22.33.22.11").signed(true)).imageTag("contract123.jpg");
What if you also need fine-grained control over not only the device accessing specific resources or when they are available, but also the specific person or people accessing them?
For example, let’s say you are developing a website that has a membership-only section for registered users. The registered users have access to images and videos that should not be available to your 'regular' visitors. You also want to grant access for up to 1 hour while ensuring that your registered users can't just share an image URL with non-members. In this case you will want to use the cookie-based authentication feature, which expands on the functionality of token-based authentication, enabling you to limit the delivery of authenticated images only to users with a valid cookie. The validity of the cookie can be matched with the user's session expiration and can include an Access Control List (ACL) for configuring the URL path where the cookie can be used (e.g., /image/authenticated/*). As with token-based authentication, you can also limit the cookie by IP address and/or time.
In the example above, when a user logs in to the membership section of your website, you would generate a cookie for the user that allows him to view your "authenticated" images. But if anyone without the cookie tried to open the same image URL they would get an error.
Example: To create a cookie that is valid for 60 minutes, allows access to all of your authenticated images, and is signed with MY_KEY:
tokenOptions = { :key => "MY_KEY", :duration => 3600, :acl => "/image/authenticated/*" } cookieToken = Cloudinary::Utils.generate_auth_token(tokenOptions)
The flexibility of cookie-based authentication also provides a means for customizing the access for every user that logs into your system. As Cloudinary uses dynamic URLs to deliver your resources, the Access Control List path in the cookie can include transformations that are custom-tailored with details specific to your company, for example, adding the company logo as an overlay on an image (e.g., /image/authenticated/l_logo.png/*). That means that the cookie can only be used to authenticate URLs that include the given transformation.
For example, you could set the ACL in the cookie so that it authorizes all images with a width of 700 pixels and a company logo in the top right corner:
:acl => "/image/authenticated/w_700/l_logo,g_north_east/*"
To simplify the effort, you can group any set of transformations as a named transformation and then simply add the named transformation to the ACL. For example, adding a named transformation called "authorized" to the ACL, where "authorized" has been set to the transformations mentioned above: (w_700/l_logo,g_north_east
).
:acl => "/image/authenticated/t_authorized/*"
One of the potential drawbacks of delivering even authenticated images is the ease with which the recipient can simply save the image to their computer and potentially pass it on to anyone else. This can be especially problematic if your company needs specific people to view your resources, but the resources also need to be kept private from others for a variety of reasons, for example, images of pre-released products. To help safeguard against any "leaks", you can provide a means of tracing where the image originated.
Let’s say that John Doe is a product design manager. He needs to have access to all the images being prepared for a planned product release. Here's an example that allows authentication for any image as long as that person's name is added as a semi-transparent text overlay, repeated over the entire image.
:acl => "/image/authenticated/a_-25,o_6,l_text:Arial_25_bold:" + current_user + "/fl_tiled.layer_apply/*"
So the following image URL will be authenticated for John Doe, but not for any other user:
http://res.cloudinary.com/demo/image/authenticated/a_-25,l_text:Arial_25_bold:John%20Doe,o_6/fl_tiled.layer_apply/w_800/cookies.jpg
cl_image_tag("cookies.jpg", :sign_url=>true, :type=>"authenticated", :transformation=>[ {:angle=>-25, :overlay=>"text:Arial_25_bold:John%20Doe", :opacity=>6}, {:flags=>["tiled", "layer_apply"]} ])
cl_image_tag("cookies.jpg", array("sign_url"=>true, "type"=>"authenticated", "transformation"=>array( array("angle"=>-25, "overlay"=>"text:Arial_25_bold:John%20Doe", "opacity"=>6), array("flags"=>array("tiled", "layer_apply")) )))
CloudinaryImage("cookies.jpg").image(sign_url=True, type="authenticated", transformation=[ {"angle": -25, "overlay": "text:Arial_25_bold:John%20Doe", "opacity": 6}, {"flags": ["tiled", "layer_apply"]} ])
cloudinary.image("cookies.jpg", {sign_url: true, type: "authenticated", transformation: [ {angle: -25, overlay: "text:Arial_25_bold:John%20Doe", opacity: 6}, {flags: ["tiled", "layer_apply"]} ]})
cloudinary.url().transformation(new Transformation() .angle(-25).overlay("text:Arial_25_bold:John%20Doe").opacity(6).chain() .flags("tiled", "layer_apply")).signed(true).type("authenticated").imageTag("cookies.jpg")
$.cloudinary.image("cookies.jpg", {type: "authenticated", transformation: [ {angle: -25, overlay: "text:Arial_25_bold:John%20Doe", opacity: 6}, {flags: ["tiled", "layer_apply"]} ]})
cloudinary.Api.UrlImgUp.Transform(new Transformation() .Angle(-25).Overlay("text:Arial_25_bold:John%20Doe").Opacity(6).Chain() .Flags("tiled", "layer_apply")).Signed(true).Type("authenticated").BuildImageTag("cookies.jpg")
This very identifying watermark can help ensure that John will not break company policy and share his image with others.
Preventing unauthenticated access to your resources can be an important concern for a variety of reasons. With cookies you can take your authentication workflow to a whole new level of customizable access to your resources, including the flexibility to tailor the access on an individual basis for every authenticated user. For more detailed information on authenticating resources and implementing cookie-based authentication, see the documentation on authenticated images.
The new authentication functionality is currently available for our Advanced plan and above customers and requires a small setup on Cloudinary's side. Contact us to enable these features for your account.
Cloudinary reduces the complexities of image manipulation, storage, administration and delivery and provides developers with an easy-to-use, end-to-end, cloud-based solution. Cloudinary enables developers to focus on creating their apps and presenting the best images possible. If you haven't tried Cloudinary yet, what are you waiting for? Visit our website to sign up for a free account.
cl_image_tag("blog_img_loading_reloaded.png")
cl_image_tag("blog_img_loading_reloaded.png")
CloudinaryImage("blog_img_loading_reloaded.png").image()
cloudinary.image("blog_img_loading_reloaded.png")
cloudinary.url().imageTag("blog_img_loading_reloaded.png")
$.cloudinary.image("blog_img_loading_reloaded.png")
cloudinary.Api.UrlImgUp.BuildImageTag("blog_img_loading_reloaded.png")
In the world of web design, what you don’t see can hurt you. Worse, it can damage your brand reputation, bottom line, or both.
Specifically I’m talking about images. Images can consume a lot of bandwidth (upwards of 70% of it for some sites). You get charged to send them. Your users are are charged to view them. In fact, you’re both probably getting charged for images that are never seen because website visitors never scroll down far enough to view them.
Cloudinary can help eliminate the bandwidth challenges associated with website images. In this blog, I’ll share three popular solutions and discuss the merits and pitfalls of each.
It’s also worth noting that images don’t just affect the actual performance of the site, but the perceived performance as well. They can have a major impact on how fast your site feels, which may have a greater impact on conversions than the actual loading time.
Let’s take a quick look at a few use cases that cause small headaches for front-end developers:
The bigger and more popular the site, the more acute these issues become. Waiting for tons of images to load on slow connections is annoying. Waiting for them to load and finding out that you’ve run out of data on your mobile plan is frustrating.
To ensure your site isn’t bloated, and your users can quickly download the images they want to see, there are some popular solutions for developers:
Now, which solution is the “best”? You can probably tell from my use of quotation marks that there is no “best.” Often you have to use what is best for the job at hand. Naturally, I have some opinions on that. If you disagree, or just have a somewhat different perspective, I encourage you to discuss this in the comments below.
Let’s talk about preloaded images first, as it’s the most situational. You’ll see them used a lot in highly creative designs, especially ones that have video backgrounds, or many large images, such as artists’ and designers’ portfolios.
Does your site have one of those big image carousels I mentioned? Maybe the carousel spans the entire width and height of the page? Then preloading makes sense...kind of. It’s best, when at all possible, to build a user experience that can finish loading as-needed. Forcing your users to wait for a preloader wasn’t cool in the days of animated splash screens, and it’s still not that cool now.
But life isn’t perfect, and sometimes you just need that big carousel. It could actually be the best solution for the problem at hand , or more likely a client request. The point is, if you have to do it, you may as well do it with a preloader. Carousels that show images still loading slowly are worse than carousels with a progress bar.
Up to this point, we’ve discussed preloaders as they relate to UX and UI design. We’ve discussed them in terms of progress bars and loading screens. But that’s just one approach. You can preload content without ever making the user wait.
Mark Meyer has a fantastic method for preloading images in a way that makes sense, especially for image sliders and galleries. It involves using a preloader to download the images that the user is most likely to want to see next, resulting in a much more seamless experience. This approach is achieved through a healthy mix of both parallel and sequential downloads.
Keep in mind that this solution relies heavily on JavaScript, and so can break down a little more easily on bad connections and slow devices.
You also might consider using <link rel="preload"> for some assets instead of pure JavaScript. It’s currently only supported in Chrome, Opera and the Android browser, so it might be best used in internal projects. But it is one more option available in the preloading arsenal.
And now we get to the meat of this discussion. Lazy loading and progressive images are far more flexible options than preloading, and could potentially be used in almost every website. The more images you have, the more you’ll want to consider using one of these techniques. This is true not just for image galleries, but for newspaper sites, blogs, eCommerce, and more.
Lazy loading is by far the most byte-efficient technique on this list. What never gets loaded will never cost you in terms of bandwidth. It’s that simple. If traffic is your biggest concern, lazy loading is the way to go.
The potential complications lie in the implementation of lazy loading. Currently, it almost always requires a lot more JavaScript than other methods. More JavaScript means more complexity, more development time and more chances for something to go wrong.
That leads us to the last major complication: Lazy loading is an all-or-nothing game, most of the time. If not all the JavaScript is downloaded over a slow 3G connection, or if not all of the JavaScript is executed, your images might not show up at all. If it doesn’t work, none will be able to see the pictures below the fold...or above the fold either, depending on how you implemented it.
Lazy loading is ideal for those situations where your target demographic is known and well understood, and they mostly use devices with good connections and decent processors. We’re talking about mid- to high-end smartphones on fast networks, and laptop or desktop devices on broadband connections. If you cannot rely on most of your users to have these things, you’re better off depending on JavaScript as little as possible.
Lazy loading non-essential images in a blog post is a great idea. This approach will work in your photography portfolio too, most of the time. Lazy loading product shots in an online shop might backfire, if someone who needs to order your product can’t find it. This is especially true if they’re on a mobile device with a bad connection.
Images that are essential content should remain small when possible, and loaded as normally as possible. Non-essential images are always fair game for lazy loading.
Fortunately, there are workarounds for the potential pitfalls of this approach. In this article, Robin Osborne presents a lazy loading solution with a non-JavaScript fallback.
If you’re already depending on JavaScript, you can use a library like lazysizes. It will take care of the lazy loading and generate responsive images on-the-fly.
Progressive images, sometimes called “image previews,” or “low-quality image placeholders,” are coming into their own. As mentioned before, we’ve seen the rise of progressive images on sites like Medium, Facebook and others where images are at least as important as the text, and not showing the image is simply never an option.
Progressive images scratch a different itch altogether: perceived performance. As I mentioned earlier, websites can’t just be fast, they have to feel fast. In this era of the microsecond-long attention span, you need to keep people around long enough for your images to load in the first place. On sites where images make up a majority of the content, that means letting people know there are images to see, if only they’ll wait a moment.
They usually do this by taking a much smaller, blurry version of the image, and showing it first. Once the high-resolution version loads, it fades into place. If your user got caught up reading the text of, say, a Medium post, they may be none the wiser. The first impression is the one that matters, and progressive images can help you to optimize that impression.
Other benefits of this approach include:
As mentioned, progressive images are used primarily for sites where you need visitors to know that images are coming. Consider the eCommerce example again: When a page full of product shots first loads, you want people to wait around long enough for them to load. Social networks are another good example: You want visitors to see their news feed actively loading, and if parts of it look empty, they may get frustrated and leave.
Still worried about the bytes? Use this technique with responsive images, and you’re pretty much good to go. All you’d have to do is load up the image preview first, and then use an image tag with the srcset attribute. It’s an extra step, and requires generating the different sizes for every image, but that can all be automated.
Being lazy (when loading images) is a virtue when bytes are all that matter, and you know your users can handle the problems that come with lazy loading. Progressive images might make a better first impression, though.
I recommend using lazy loading non-essential images, mostly. You could do it with every image, so long as you have a fallback. It’s best for users that you know are on good connections, and for situations where saving bandwidth is paramount.
Progressive images should be used for all sites where images are a non-negotiable part of the experience. Examples include product shots, social news feeds, articles where the photos are necessary for context, and tutorials.
Preloading your images is best used when you have no other choice, as it has all of the potential drawbacks of lazy loading, and uses more bandwidth by default.
In the end, there’s nothing actually stopping you from preloading the first three images in a carousel, and lazy loading the rest with blurry previews just in case. Well, nothing except perhaps the overbearing complexity of that solution. My point is that you don’t have to stick with just one. Evaluate your approach to every situation, as well as to different elements of the same project.
You might be surprised by what you discover. And when you do come up with something cool, let us know in the comments.
Ezequiel Bruni is a web/UX designer, writer, and aspiring e-sports commentator. When he's not up to his finely-chiseled ears in wire-frames and front-end code, or ranting about the same, he indulges in video games, beer, pizza, video games, fantasy novels, stand-up comedy, and video games.. |
cl_image_tag("Ajax-Blog-Cover_2000x1100.jpg", :transformation=>[ {:width=>770, :crop=>"fill"}, {:dpr=>1.0} ])
cl_image_tag("Ajax-Blog-Cover_2000x1100.jpg", array("transformation"=>array( array("width"=>770, "crop"=>"fill"), array("dpr"=>1.0) )))
CloudinaryImage("Ajax-Blog-Cover_2000x1100.jpg").image(transformation=[ {"width": 770, "crop": "fill"}, {"dpr": 1.0} ])
cloudinary.image("Ajax-Blog-Cover_2000x1100.jpg", {transformation: [ {width: 770, crop: "fill"}, {dpr: 1.0} ]})
cloudinary.url().transformation(new Transformation() .width(770).crop("fill").chain() .dpr(1.0)).imageTag("Ajax-Blog-Cover_2000x1100.jpg")
$.cloudinary.image("Ajax-Blog-Cover_2000x1100.jpg", {transformation: [ {width: 770, crop: "fill"}, {dpr: 1.0} ]})
cloudinary.Api.UrlImgUp.Transform(new Transformation() .Width(770).Crop("fill").Chain() .Dpr(1.0)).BuildImageTag("Ajax-Blog-Cover_2000x1100.jpg")
In this post, I’ll show you two ways of handling file uploads with AJAX:
Cloudinary is an end-to-end solution for all your image and video-related needs, which enables you to securely upload images or any other file at any scale from any source. Cloudinary also provides an API for fast upload directly from your users’ browsers or mobile apps. Check out Cloudinary’s documentation for more information on how to integrate it in your apps.
First, let’s look at how to handle file uploads with AJAX, the barebones way. We need:
You need to build up an HTML form that will contain the fields the user will interact with to upload a file. Create an index.html
file in your root directory and the following code:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>File Upload with AJAX</title> </head> <body> <form id="file-form" action="fileUpload.php" method="post" enctype="multipart/form-data"> Upload a File: <input type="file" id="myfile" name="myfile"> <input type="submit" id="submit" name="submit" value="Upload File Now" > </form> <p id="status"></p> <script type="text/javascript" src="fileUpload.js"></script> </body> </html>
In the code above, we have a form with one input field and a submit button. Now, the form tag has an action attribute that points to the script that will take care of the actual upload process. It also has a method attribute that specifies the kind of operation this form will undertake, which is a POST action.
The value of the enctype attribute is very important. It determines the content type that the form submits. If we were not dealing with file uploads, then we would not specify the enctype attribute on the form in most cases.
The two fields also have an id attribute. This attribute will enable us to handle the form elements with JavaScript.
Create a file in the root directory, fileUpload.js and add this code to it like so:
fileUpload.js
(function(){ var form = document.getElementById('file-form'); var fileSelect = document.getElementById('myfile'); var uploadButton = document.getElementById('submit'); var statusDiv = document.getElementById('status'); form.onsubmit = function(event) { event.preventDefault(); statusDiv.innerHTML = 'Uploading.......'; // Get the files from the input var files = fileSelect.files; // Create a new FormData object. var formData = new FormData(); //Grab just one file, since we are not allowing multiple file uploads var file = files[0]; //Check the file type if (!file.type.match('image.*')) { statusDiv.innerHTML = 'This file is not an image. Sorry, it cannot be uploaded. Try again with a valid image.'; return; } if (file.size >= 2000000 ) { statusDiv.innerHTML = 'This file is larger than 2MB. Sorry, it cannot be uploaded.'; return; } // Add the file to the request. formData.append('myfile', file, file.name); // Set up the AJAX request. var xhr = new XMLHttpRequest(); // Open the connection. xhr.open('POST', '/fileUpload.php', true); // Set up a handler for when the request finishes. xhr.onload = function () { if (xhr.status === 200) { statusDiv.innerHTML = 'The file uploaded successfully.......'; } else { statusDiv.innerHTML = 'An error occurred while uploading the file. Try again'; } }; // Send the Data. xhr.send(formData); } })();
Let’s analyze this piece of code above. First thing we did was grab all the elements, the form, file input element and status div like so:
var form = document.getElementById('file-form'); var fileSelect = document.getElementById('myfile'); var statusDiv = document.getElementById('status');
The next thing we need is to call the form’s onsubmit event. When the user submits the form, this is the event that gets fired.
form.onsubmit = function(event) { …. }
Now that we have attached an event handler to the form, we need to get the file that the user selects and, most importantly, let the user know that something is going on behind the scenes like so:
... statusDiv.innerHTML = 'Uploading.......'; // Get the files from the input var files = fileSelect.files; // Grab just one file, since we are not allowing multiple file uploads var file = files[0]; ...
Moving forward, we create a form object, validate the size and type of file that the user selects, and also add the file to the form object.
// Create a new FormData object. var formData = new FormData(); //Check the file type if (!file.type.match('image.*')) { statusDiv.innerHTML = 'This file is not an image. Sorry, it can’t be uploaded. Try again with a valid image.'; return; } if (file.size >= 2000000 ) { statusDiv.innerHTML = 'This file is larger than 2MB. Sorry, it can’t be uploaded.'; return; } // Add the file to the request. formData.append('myfile', file, file.name);
Finally, we set up the AJAX request, open a connection and listen on the onload event of the xhr object.
... // Set up the AJAX request. var xhr = new XMLHttpRequest(); // Open the connection. xhr.open('POST', '/fileUpload.php', true); // Set up a handler for when the request finishes. xhr.onload = function () { if (xhr.status === 200) { statusDiv.innerHTML = 'The file has been Uploaded Successfully.......'; } else { statusDiv.innerHTML = 'An error occurred while uploading the file...Try again'; } }; // Send the Data. xhr.send(formData);
Here, we are making a post request to fileUpload.php. Yes, we still need to handle and process the file on the backend. The AJAX request submits the files to the backend. Now, it’s time for the backend to process it.
Note: There are several edge cases you should consider handling if you want to use this code in production. You will want to require more checks to ensure safe files are posted to your backend.
Since we are focused on file upload via AJAX request in this post, I’ll simply place the PHP code required for upload without explanation.
<?php $currentDir = getcwd(); $uploadDirectory = "/uploads/"; $errors = []; // Store all foreseen and unforseen errors here $fileExtensions = ['jpeg','jpg','png']; // Get all the file extensions $fileName = $_FILES['myfile']['name']; $fileSize = $_FILES['myfile']['size']; $fileTmpName = $_FILES['myfile']['tmp_name']; $fileType = $_FILES['myfile']['type']; $fileExtension = strtolower(end(explode('.',$fileName))); $uploadPath = $currentDir . $uploadDirectory . basename($fileName); echo $uploadPath; if (isset($fileName)) { if (! in_array($fileExtension,$fileExtensions)) { $errors[] = "This file extension is not allowed. Please upload a JPEG or PNG file"; } if ($fileSize > 2000000) { $errors[] = "This file is more than 2MB. Sorry, it has to be less than or equal to 2MB"; } if (empty($errors)) { $didUpload = move_uploaded_file($fileTmpName, $uploadPath); if ($didUpload) { echo "The file " . basename($fileName) . " has been uploaded"; } else { echo "An error occurred somewhere. Try again or contact the admin"; } } else { foreach ($errors as $error) { echo $error . "These are the errors" . "\n"; } } } ?>
Note: Ensure you are running a PHP server like so:
cl_image_tag("ajax_image1.png", :width=>650, :crop=>"scale", :secure=>true)
cl_image_tag("ajax_image1.png", array("width"=>650, "crop"=>"scale", "secure"=>true))
CloudinaryImage("ajax_image1.png").image(width=650, crop="scale", secure=True)
cloudinary.image("ajax_image1.png", {width: 650, crop: "scale", secure: true})
cloudinary.url().transformation(new Transformation().width(650).crop("scale")).secure(true).imageTag("ajax_image1.png")
$.cloudinary.image("ajax_image1.png", {width: 650, crop: "scale", secure: true})
cloudinary.Api.UrlImgUp.Transform(new Transformation().Width(650).Crop("scale")).Secure(true).BuildImageTag("ajax_image1.png")
Run the application. It should come up like so:
cl_image_tag("ajax_upload_file.png", :width=>650, :crop=>"scale")
cl_image_tag("ajax_upload_file.png", array("width"=>650, "crop"=>"scale"))
CloudinaryImage("ajax_upload_file.png").image(width=650, crop="scale")
cloudinary.image("ajax_upload_file.png", {width: 650, crop: "scale"})
cloudinary.url().transformation(new Transformation().width(650).crop("scale")).imageTag("ajax_upload_file.png")
$.cloudinary.image("ajax_upload_file.png", {width: 650, crop: "scale"})
cloudinary.Api.UrlImgUp.Transform(new Transformation().Width(650).Crop("scale")).BuildImageTag("ajax_upload_file.png")
Index Page
Try uploading a file that is higher than 2MB. You should get this:
cl_image_tag("ajax_file_upload.png", :width=>650, :crop=>"scale")
cl_image_tag("ajax_file_upload.png", array("width"=>650, "crop"=>"scale"))
CloudinaryImage("ajax_file_upload.png").image(width=650, crop="scale")
cloudinary.image("ajax_file_upload.png", {width: 650, crop: "scale"})
cloudinary.url().transformation(new Transformation().width(650).crop("scale")).imageTag("ajax_file_upload.png")
$.cloudinary.image("ajax_file_upload.png", {width: 650, crop: "scale"})
cloudinary.Api.UrlImgUp.Transform(new Transformation().Width(650).Crop("scale")).BuildImageTag("ajax_file_upload.png")
Try uploading a file that is not an image. You should get this:
cl_image_tag("ajax_file_upload_complete.png", :width=>650, :crop=>"scale")
cl_image_tag("ajax_file_upload_complete.png", array("width"=>650, "crop"=>"scale"))
CloudinaryImage("ajax_file_upload_complete.png").image(width=650, crop="scale")
cloudinary.image("ajax_file_upload_complete.png", {width: 650, crop: "scale"})
cloudinary.url().transformation(new Transformation().width(650).crop("scale")).imageTag("ajax_file_upload_complete.png")
$.cloudinary.image("ajax_file_upload_complete.png", {width: 650, crop: "scale"})
cloudinary.Api.UrlImgUp.Transform(new Transformation().Width(650).Crop("scale")).BuildImageTag("ajax_file_upload_complete.png")
We checked for all these in our code, remember? Nice!
Also, it’s recommended that you upload your files to a dedicated file server, not just your web application server. Check out the source code for this tutorial.
Cloudinary provides an API for uploading images and any other kind of files to the cloud. These files are safely stored in the cloud with secure backups and revision history, as opposed to the sample above where the file is uploaded directly to the web server and the server stores the file.
Cloudinary provides a free tier in which you can store up to 75,000 images and videos with managed storage of 2GB. In this free tier, you also receive 7,500 monthly transformations and 5GB monthly net viewing bandwidth.
In a situation where you need to store the files in the cloud, this would be the typical scenario without Cloudinary:
While this approach works, you can actually do a direct upload that eliminates the possibilities of redundant uploads. With Cloudinary, direct file upload to the cloud is painless.
The process is this simple.
1. Sign up for a Cloudinary account
cl_image_tag("sign_up.png", :width=>650, :crop=>"scale")
cl_image_tag("sign_up.png", array("width"=>650, "crop"=>"scale"))
CloudinaryImage("sign_up.png").image(width=650, crop="scale")
cloudinary.image("sign_up.png", {width: 650, crop: "scale"})
cloudinary.url().transformation(new Transformation().width(650).crop("scale")).imageTag("sign_up.png")
$.cloudinary.image("sign_up.png", {width: 650, crop: "scale"})
cloudinary.Api.UrlImgUp.Transform(new Transformation().Width(650).Crop("scale")).BuildImageTag("sign_up.png")
The Cloudinary dashboard ...Your cloud name, API key, and API secret are key valuables to interact with Cloudinary functionalities.
After you set up a Cloudinary account, you can create an upload preset. Upload presets enable you to centrally define a set of image upload options, instead of specifying them in each upload call. We will use the preset when making an upload request.
2. Fetch the Cloudinary JS library
Cloudinary provides JavaScript plugins that make image upload to the Cloudinary server very easy. Include them like so in your index.html file:
... <script src='https://cdn.jsdelivr.net/jquery.cloudinary/1.0.18/jquery.cloudinary.js' type='text/javascript'></script> <script src="//widget.cloudinary.com/global/all.js" type="text/javascript"></script> <script src="/script.js"></script> ...
3. Direct Upload
Replace the content of fileUpload.js with this:
$(function() { // Configure Cloudinary // with credentials available on // your Cloudinary account dashboard $.cloudinary.config({ cloud_name: 'YOUR_CLOUD_NAME', api_key: 'YOUR_API_KEY'}); // Upload button var uploadButton = $('#submit'); // Upload button event uploadButton.on('click', function(e){ // Initiate upload cloudinary.openUploadWidget({ cloud_name: 'YOUR_CLOUD_NAME', upload_preset: 'YOUR_UPLOAD_PRESET', tags: ['cgal']}, function(error, result) { if(error) console.log(error); // If NO error, log image data to console var id = result[0].public_id; console.log(processImage(id)); }); }); }) function processImage(id) { var options = { client_hints: true, }; return '<img src="'+ $.cloudinary.url(id, options) +'" style="width: 100%; height: auto"/>'; }
It’s that simple. Make sure you replace your cloud_name, upload_preset and api_key with the right values from your Cloudinary dashboard.
We have successfully covered how to handle file uploads with AJAX. Implementation of AJAX can be very tricky, but with the Cloudinary jQuery library, you can implement it easily in your application.
It is also highly recommended to offload files to dedicated cloud storage services, like Cloudinary, to solve the issue of latency and enable effective storage. Let Cloudinary handle the complexities of serving the files securely to your web app.
Prosper Otemuyiwa is a Food Ninja, Open Source Advocate & Self-acclaimed Developer Evangelist. |
cl_image_tag("cloudinary_imagemagick")
cl_image_tag("cloudinary_imagemagick")
CloudinaryImage("cloudinary_imagemagick").image()
cloudinary.image("cloudinary_imagemagick")
cloudinary.url().imageTag("cloudinary_imagemagick")
$.cloudinary.image("cloudinary_imagemagick")
cloudinary.Api.UrlImgUp.BuildImageTag("cloudinary_imagemagick")
Most developers are familiar with ImageMagick, a very capable open source software suite that they can use to manage and manipulate images.
The functionality of ImageMagick is typically utilized from the command-line, but wrappers have been written for a wide selection of programming languages, making ImageMagick a very popular choice among developers.
Often programmers use ImageMagick for simple operations such as scaling or stretching an image to fit specific dimensions. But as we'll see in this post, there are many advanced techniques available, such as 'masking’, pixel 'blending' and others that enable you to achieve more complex and extremely useful effects.
In this post, I’ll share three examples of image manipulations using RMagick (a Ruby wrapper for ImageMagick). I will start with a sepia effect combined with an overlay image, and follow that with increasingly complex manipulations such as creating text textures and pixelating faces. For each of these examples, I’ll compare what it takes to address the same challenges using Cloudinary. With its cloud-based platform, Cloudinary eliminates the complexity of coding and the hassles associated with installation. Cloudinary also saves developers time, enabling them to leverage a single line of code to complete the image manipulations.
You can find the full source code for this blog post in Cloudinary’s official repository in Github. Installing the dependencies for the examples is not a trivial task, mostly because of OpenCV. But for simplicity’s sake, I’ve containerized both ImageMagick and OpenCV dependencies, so you only need to have Docker installed.
The first example describes a common use case: Adding a photographer's name to the bottom right corner of an image.
To make the example more interesting, we first want to apply a sepia effect.
The final result should look something like this:
The example is built using two main methods:
add_author_to_image!
, which receives a Magick::Image
object and the author
name and returns a new Magick::Image
object that contains the resulting image - the original image with the author name overlay applied.sepiatize_image!
, which receives a Magick::Image
object and returns the modified image object with the sepia effect applied.First, let’s describe add_author_to_image!
:
Using ImageMagick, I create a Draw
object, which will enable me to put the text over the original image.
I also select the color “white”, “Arial” as the font, and choose my font size.
text = Magick::Draw.new text.fill = "white" text.font_family = "Arial" text.pointsize = 30 text.gravity = Magick::CenterGravity
The overlay image size should match the text dimensions, so I need to calculate the text dimensions before creating the overlay image:
metrics = text.get_type_metrics(author)
I then create the overlay image and set its background color to a semi-transparent black to ensure that the text will be visible on top of any image.
author_overlay = Magick::Image.new(metrics.width, metrics.height) { self.background_color = "#00000080" }
Next, I draw the text over the the semi-transparent background.
text.annotate(author_overlay, 0, 0, 0, 0, author)
Then I position the author overlay on top of the image:
I use the composite
method to combine the two images in a certain way. The destination image is our input image. I have to tell ImageMagick that I want to position the author overlay in the “southeast” edge of the image.
Magick::OverCompositeOp
tells ImageMagick that the source image (author_overlay
) should override the destination image (our input image) with the source image’s pixels, which makes the author name visible on top of the image.
image.composite!(author_overlay, Magick::SouthEastGravity, Magick::OverCompositeOp)
The final step is destroying the author image object overlay. It’s important to clean up any temporary image objects. I don’t need the overlay anymore because I’ve combined the images and flattened them. Destroying an image object is done using the destroy!
method:
author_overlay.destroy!
What’s missing now is the sepia effect (applied by the sepiatize_image!
method).
ImageMagick offers a method for setting sepia tone, so I chose the sepia level and set the desired effect.
SEPIA_LEVEL = Magick::QuantumRange * 60 / 100 def sepiatize_image!(image) image.sepiatone(SEPIA_LEVEL) end
After defining the two methods, we need to apply both operations on our original image and write the result to a file:
add_author_to_image!(sepiatize_image!(source_image), author).write(dest)
Whew! I’m sweating a bit, but I managed to get the result I wanted.
If this is your first time using Cloudinary, you’ll need sign up for an account (start with a free one), and use the upload API to upload your image. You can use the https API with no installation required, or download and install the SDK for your favorite framework (a few quick configurations required).
Here’s how you would upload the yellow_tulip.png file to your account in the cloud using the Ruby SDK:`
Cloudinary::Uploader.upload('yellow_tulip.png', :public_id => 'yellow_tulip')
Now, let’s see what it takes to deliver that image in sepia with the photographer’s name in the corner. Ready?
cl_image_tag("yellow_tulip.png", :secure=>true, :transformation=>[ {:effect=>"sepia:60"}, {:overlay=>"text:Arial_30:John%20Doe%20Photography%20%C2%AE", :color=>"white", :gravity=>"south_east", :background=>"#00000090"} ])
cl_image_tag("yellow_tulip.png", array("secure"=>true, "transformation"=>array( array("effect"=>"sepia:60"), array("overlay"=>"text:Arial_30:John%20Doe%20Photography%20%C2%AE", "color"=>"white", "gravity"=>"south_east", "background"=>"#00000090") )))
CloudinaryImage("yellow_tulip.png").image(secure=True, transformation=[ {"effect": "sepia:60"}, {"overlay": "text:Arial_30:John%20Doe%20Photography%20%C2%AE", "color": "white", "gravity": "south_east", "background": "#00000090"} ])
cloudinary.image("yellow_tulip.png", {secure: true, transformation: [ {effect: "sepia:60"}, {overlay: "text:Arial_30:John%20Doe%20Photography%20%C2%AE", color: "white", gravity: "south_east", background: "#00000090"} ]})
cloudinary.url().transformation(new Transformation() .effect("sepia:60").chain() .overlay("text:Arial_30:John%20Doe%20Photography%20%C2%AE").color("white").gravity("south_east").background("#00000090")).secure(true).imageTag("yellow_tulip.png")
$.cloudinary.image("yellow_tulip.png", {secure: true, transformation: [ {effect: "sepia:60"}, {overlay: "text:Arial_30:John%20Doe%20Photography%20%C2%AE", color: "white", gravity: "south_east", background: "#00000090"} ]})
cloudinary.Api.UrlImgUp.Transform(new Transformation() .Effect("sepia:60").Chain() .Overlay("text:Arial_30:John%20Doe%20Photography%20%C2%AE").Color("white").Gravity("south_east").Background("#00000090")).Secure(true).BuildImageTag("yellow_tulip.png")
Done!
In this example, which is a bit more complex than our first one, I will use our source image as a texture effect on specified text with a selected font, so that it will look something like this:
I will employ the concept of masking in this example.
Let's create a 'mask' image in the shape of our desired text and font. I do this using the create_text_mask
method, which receives the text as a string and returns a 'mask' Image
object.
The following is a short walkthrough:
Like in the previous example, I create a Draw
object, which is being used for the text.
I then add color, font family, size and weight, and get a type metric to create our ‘mask’ image object with the dimensions I calculated.
Then I create a new Image
object and set it's background to transparent:
draw = Magick::Draw.new draw.fill = "white" draw.font_family = "Arial" draw.pointsize = 100 draw.font_weight = Magick::BoldWeight draw.gravity = Magick::CenterGravity metrics = draw.get_type_metrics(text) mask = Magick::Image.new(metrics.width, metrics.height) { self.background_color = "transparent" }
Like in the previous example, I use the draw object’s annotate method to actually draw the text over the image I’ve created.
draw.annotate(mask, 0, 0, 0, 0, text)
The result is a semi transparent image having the text itself in fully opaque "white" and the rest - fully transparent.
Let's move to the cut_image_to_text!
method which calls create_text_mask
:
After creating the mask image by calling create_text_mask
, using the composite
method, I combine the 'mask' image and our input image, but this time relying on a different type of composite operator: CopyOpacityCompositeOp
.
Doing this changes the opacity value of all pixels in our input image that are not part of the text letters region, to fully transparent. This hides the pixels and results in a texture effect over the fonts:
image.composite!(mask, Magick::CenterGravity, Magick::CopyOpacityCompositeOp)
Next, I have to clean up by destroying the mask
image, which isn't needed anymore.
mask.destroy!
Then I trim the remaining transparent border and tighten up the image to the text itself.
image.trim!
At this point I’m almost done, but there’s one last thing to take care of.
A lot of formats – like JPEG – don’t have an alpha channel to support opacity. So, if you export the image now in one of those non-alpha formats, you’ll see the original image since all 'hidden' and 'fully transparent' pixels from our last step will not preserve their transparency. As a result, I’ll need to turn all transparent pixels to “opaque white”, which will make sure I get a uniform result no matter what the desired format is. This will be done using the simple opacify!
method.
Here I set the background color to white:
opacified = Magick::Image.new(image.columns, image.rows) { self.background_color = "white" }
Then I put the cut-to-text image that I created in the previous step on top of the new image with the white background color. This causes all transparent pixels from before to turn white.
opacified.composite!(image, Magick::CenterGravity, Magick::OverCompositeOp)
After defining the relevant methods, I apply them on the original image and write the result to a file:
opacify!(cut_image_to_text!(source, text)).write(dest)
Impressive! We did it!
You’d think this example would take a bit more effort than the previous one, even in Cloudinary. But with a combination of text overlay capabilities and the built-in cutter
flag, here’s all we need to do:
cl_image_tag("yellow_tulip.png", :overlay=>"text:Arial_100_bold:Flowers", :flags=>"cutter", :secure=>true)
cl_image_tag("yellow_tulip.png", array("overlay"=>"text:Arial_100_bold:Flowers", "flags"=>"cutter", "secure"=>true))
CloudinaryImage("yellow_tulip.png").image(overlay="text:Arial_100_bold:Flowers", flags="cutter", secure=True)
cloudinary.image("yellow_tulip.png", {overlay: "text:Arial_100_bold:Flowers", flags: "cutter", secure: true})
cloudinary.url().transformation(new Transformation().overlay("text:Arial_100_bold:Flowers").flags("cutter")).secure(true).imageTag("yellow_tulip.png")
$.cloudinary.image("yellow_tulip.png", {overlay: "text:Arial_100_bold:Flowers", flags: "cutter", secure: true})
cloudinary.Api.UrlImgUp.Transform(new Transformation().Overlay("text:Arial_100_bold:Flowers").Flags("cutter")).Secure(true).BuildImageTag("yellow_tulip.png")
For my last example, I will show how to combine face detection with a pixelation effect, which is really useful for hiding people’s identities in public photos. Here’s what our final image will look like:
This example involves use of the OpenCV library and the ruby-opencv gem. My code in this example is written in a flexible way that enables easy swapping of the face detection part to any other library or implementation (modify the detect_faces
method to one of your liking, but you must make sure it returns an array of regions).
First, let’s have a look at the transform_faces
method.
I’m creating a Magick:Image
object out of this file, which I took from the user’s input, similar to the way I’ve done in our first two examples.
image = Magick::Image.read(image_file).first
Then I detect faces for the image file. This process, which is done outside of ImageMagick, returns a collection of face regions.
detect_faces(image_file).each do |region| transform_region!(image, region, operation) end
Next, for each region, I call transform_region!
over the image object. The transform_region
method has top_left
and bottom_right
attributes, each with an X
,Y
value. Here I crop a new image out of the original image at the exact region that was identified.
cropped_region = image.crop(region.top_left.x, region.top_left.y, region.width, region.height)
Then I call a method for the operation that I want to do with this cropped region image.
cropped_region = send("#{operation}_image!", cropped_region)
In the example code in the repository, I pick an operation called pixelate
. However, you also could select “blur” if that treatment was preferred:
PIXELATE_FACTOR = 5 def pixelate_image!(image) image.scale!(1 / PIXELATE_FACTOR.to_f).scale!(PIXELATE_FACTOR) end
Back to transform_region!
: I need to set the "transformed" region on top of the original image, using composite
again at the X
,Y
position. After that is complete, I flatten the image and then clean it up by destroying the cropped image region.
image.composite!(cropped_region, region.top_left.x, region.top_left.y, Magick::OverCompositeOp) cropped_region.destroy!
Not bad, huh?
By now, you have probably come to expect that you can accomplish the same goal with Cloudinary in one line of code, right? Yup!
Cloudinary has a built-in automatic face detection mechanism that enables, among other things, quick pixelation (or blurring) of all faces in a photo:
Pixelating faces
cl_image_tag("http://upload.wikimedia.org/wikipedia/commons/4/45/Spain_national_football_team_Euro_2012_final.jpg", :secure=>true, :type=>"fetch", :transformation=>[ {:effect=>"pixelate_faces"}, {:width=>500, :crop=>"scale"} ])
cl_image_tag("http://upload.wikimedia.org/wikipedia/commons/4/45/Spain_national_football_team_Euro_2012_final.jpg", array("secure"=>true, "type"=>"fetch", "transformation"=>array( array("effect"=>"pixelate_faces"), array("width"=>500, "crop"=>"scale") )))
CloudinaryImage("http://upload.wikimedia.org/wikipedia/commons/4/45/Spain_national_football_team_Euro_2012_final.jpg").image(secure=True, type="fetch", transformation=[ {"effect": "pixelate_faces"}, {"width": 500, "crop": "scale"} ])
cloudinary.image("http://upload.wikimedia.org/wikipedia/commons/4/45/Spain_national_football_team_Euro_2012_final.jpg", {secure: true, type: "fetch", transformation: [ {effect: "pixelate_faces"}, {width: 500, crop: "scale"} ]})
cloudinary.url().transformation(new Transformation() .effect("pixelate_faces").chain() .width(500).crop("scale")).secure(true).type("fetch").imageTag("http://upload.wikimedia.org/wikipedia/commons/4/45/Spain_national_football_team_Euro_2012_final.jpg")
$.cloudinary.image("http://upload.wikimedia.org/wikipedia/commons/4/45/Spain_national_football_team_Euro_2012_final.jpg", {secure: true, type: "fetch", transformation: [ {effect: "pixelate_faces"}, {width: 500, crop: "scale"} ]})
cloudinary.Api.UrlImgUp.Transform(new Transformation() .Effect("pixelate_faces").Chain() .Width(500).Crop("scale")).Secure(true).Type("fetch").BuildImageTag("http://upload.wikimedia.org/wikipedia/commons/4/45/Spain_national_football_team_Euro_2012_final.jpg")
Blurring faces
cl_image_tag("http://upload.wikimedia.org/wikipedia/commons/4/45/Spain_national_football_team_Euro_2012_final.jpg", :secure=>true, :type=>"fetch", :transformation=>[ {:effect=>"blur_faces"}, {:width=>500, :crop=>"scale"} ])
cl_image_tag("http://upload.wikimedia.org/wikipedia/commons/4/45/Spain_national_football_team_Euro_2012_final.jpg", array("secure"=>true, "type"=>"fetch", "transformation"=>array( array("effect"=>"blur_faces"), array("width"=>500, "crop"=>"scale") )))
CloudinaryImage("http://upload.wikimedia.org/wikipedia/commons/4/45/Spain_national_football_team_Euro_2012_final.jpg").image(secure=True, type="fetch", transformation=[ {"effect": "blur_faces"}, {"width": 500, "crop": "scale"} ])
cloudinary.image("http://upload.wikimedia.org/wikipedia/commons/4/45/Spain_national_football_team_Euro_2012_final.jpg", {secure: true, type: "fetch", transformation: [ {effect: "blur_faces"}, {width: 500, crop: "scale"} ]})
cloudinary.url().transformation(new Transformation() .effect("blur_faces").chain() .width(500).crop("scale")).secure(true).type("fetch").imageTag("http://upload.wikimedia.org/wikipedia/commons/4/45/Spain_national_football_team_Euro_2012_final.jpg")
$.cloudinary.image("http://upload.wikimedia.org/wikipedia/commons/4/45/Spain_national_football_team_Euro_2012_final.jpg", {secure: true, type: "fetch", transformation: [ {effect: "blur_faces"}, {width: 500, crop: "scale"} ]})
cloudinary.Api.UrlImgUp.Transform(new Transformation() .Effect("blur_faces").Chain() .Width(500).Crop("scale")).Secure(true).Type("fetch").BuildImageTag("http://upload.wikimedia.org/wikipedia/commons/4/45/Spain_national_football_team_Euro_2012_final.jpg")
ImageMagick is a flexible, capable, open source software suite that developers have used to manage and manipulate images for years. As we saw in this post, ImageMagick can be utilized to do so much more than simply resizing an image.
At Cloudinary, we make use of ImageMagick for a wide range of use cases, and it serves as one of the valuable tools in our toolbelt.
Cloudinary abstracts the complexities of image manipulation, storage, administration and delivery and provides developers with an easy-to-use, end-to-end, cloud-based solution. Cloudinary enables developers to focus on creating their apps and presenting the best images possible.
If you haven't tried Cloudinary yet, I invite you to test it out. Visit our website to sign up for a account
cl_image_tag("php_upload_cover_blog.jpg", :width=>700, :crop=>"scale")
cl_image_tag("php_upload_cover_blog.jpg", array("width"=>700, "crop"=>"scale"))
CloudinaryImage("php_upload_cover_blog.jpg").image(width=700, crop="scale")
cloudinary.image("php_upload_cover_blog.jpg", {width: 700, crop: "scale"})
cloudinary.url().transformation(new Transformation().width(700).crop("scale")).imageTag("php_upload_cover_blog.jpg")
$.cloudinary.image("php_upload_cover_blog.jpg", {width: 700, crop: "scale"})
cloudinary.Api.UrlImgUp.Transform(new Transformation().Width(700).Crop("scale")).BuildImageTag("php_upload_cover_blog.jpg")
There are lots of images and videos all over the internet. A lot of applications these days demand that the user is able to manipulate files and upload to the server. Thankfully, PHP provides the functions to handle file uploads.
In this post, I’ll show you two ways of handling file uploads with PHP:
Let’s quickly get started on how to handle file upload with PHP, the barebones way. We need:
You need to build up an HTML form that will contain the fields that the user will interact with to upload a file. Create an index.html
file in your root directory and add the following code to it:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>File Upload with PHP</title> </head> <body> <form action="fileUpload.php" method="post" enctype="multipart/form-data"> Upload a File: <input type="file" name="file" id="fileToUpload"> <input type="submit" name="submit" value="Upload File Now" > </form> </body> </html>
In the code above, we have a form with one input field and a submit button. The form tag has an action attribute that points to the script that will take care of the actual upload process. It also has a method attribute that specifies the kind of operation this form will undertake, which is a POST action.
The value of the enctype attribute is very important. It determines the content-type that the form submits. If we were not dealing with file uploads, then we would not specify the enctype attribute on the form in most cases.
Spin up your PHP server like so:
You should see something similar to this come up on your web page:
Note: Different browsers, such as Safari, Edge, Firefox and Chrome might display the form in different ways.
There are lots of things to consider when dealing with file uploads. I’ll highlight the common ones in form of questions.
Create a file, fileUpload.php, in the root directory and add this code to it:
<?php $currentDir = getcwd(); $uploadDirectory = "/uploads/"; $errors = []; // Store all foreseen and unforseen errors here $fileExtensions = ['jpeg','jpg','png']; // Get all the file extensions $fileName = $_FILES['myfile']['name']; $fileSize = $_FILES['myfile']['size']; $fileTmpName = $_FILES['myfile']['tmp_name']; $fileType = $_FILES['myfile']['type']; $fileExtension = strtolower(end(explode('.',$fileName))); $uploadPath = $currentDir . $uploadDirectory . basename($fileName); if (isset($_POST['submit'])) { if (! in_array($fileExtension,$fileExtensions)) { $errors[] = "This file extension is not allowed. Please upload a JPEG or PNG file"; } if ($fileSize > 2000000) { $errors[] = "This file is more than 2MB. Sorry, it has to be less than or equal to 2MB"; } if (empty($errors)) { $didUpload = move_uploaded_file($fileTmpName, $uploadPath); if ($didUpload) { echo "The file " . basename($fileName) . " has been uploaded"; } else { echo "An error occurred somewhere. Try again or contact the admin"; } } else { foreach ($errors as $error) { echo $error . "These are the errors" . "\n"; } } } ?>
Look at the code above.
In the code, we are checking to ensure that only jpeg and png files can be uploaded.
if (! in_array($fileExtension,$fileExtensions)) { $errors[] = "This file extension is not allowed. Please upload a JPEG or PNG file"; } // Checks to ensure that only jpeg and png files can be uploaded.
We are also checking that only files less than or equal to 2MB can be uploaded.
if ($fileSize > 2000000) { $errors[] = "This file is larger than 2MB. It must be less than or equal to 2MB"; } // Checks to ensure the file is not more than 2MB
Note: Before you try out your code again, you need to ensure that there are some configurations in place.
uploads/
directory is writable. Run this command: chmod 0755 uploads/
on your terminal to make the directory writable.Open your php.ini
file and ensure that these constants have correct values like:
Now, run the code again. Your file should upload successfully to the uploads directory.
Note: There are many checks you should consider when handling file uploads, including security precautions . You really don’t want someone uploading a virus to your web server. Do you? So by all means don’t use this exact code above in production. Add more valuable checks!
Also, it’s recommended that you upload your files to a dedicated file server, not just your web application server. Check out the source code for a tutorial.
Cloudinary provides an API for uploading images and any other kind of file to the cloud. These files are safely stored in the cloud with secure backups and revision history.
Cloudinary provides a free tier where you can store up to 75,000 images & videos with a managed storage of 2GB. 7500 monthly transformations and 5GB monthly net viewing bandwidth.
Cloudinary already takes away the pain of having to write large amounts of code to interact with their API by providing an open source PHP library that ships with simple, easy-to-use helper methods for:
The process is this simple!
Cloudinary Dashboard - Your cloud name, API Key, Secret are key valuables to interact with Cloudinary functionalities.
The second step to uploading your images to Cloudinary using PHP is fetching the library using composer. If you don’t have composer, navigate here, make sure you copy all required files and paste them in your app, then reference them as follows in the script where you want to perform the upload:
// importing the necessary Cloudinary files require 'Cloudinary.php'; require 'Uploader.php'; require 'Helpers.php'; require 'Api.php'; .....
You can upload images or any file to Cloudinary from your PHP code running on at least a PHP 5.3 server. First, we have to set up our key valuables using the config method, so that Cloudinary can recognize that it’s a valid account:
\Cloudinary::config(array( "cloud_name" => "my_cloud_name", "api_key" => "my_api_key", "api_secret" => "my_api_secret" ));
Note: I recommend that you load these key valuables from an environment file (.env) to avoid people getting ahold of your config details.
You can put this in a file called settings.php and just include it in the script that performs the actual uploading.
Now you can use the following methods to upload images or files to Cloudinary’s cloud platform:
Uploading a local file:
\Cloudinary\Uploader::upload("/home/my_image.jpg")
Uploading a file from a remote HTTP(s) URL:
\Cloudinary\Uploader::upload("http://www.example.com/image.jpg")
Uploading a file from an S3 bucket:
\Cloudinary\Uploader::upload('s3://my-bucket/my-path/my-file.jpg');
Note: Every file uploaded to Cloudinary is assigned a Public ID that can later be used for transformation and delivery.
The basic syntax for uploading your files with PHP to Cloudinary is:
public static function upload($file, $options = array())
Let’s cover a few examples of what can be passed to the $options array argument.
Specifying a custom Public ID:
You want to specify a custom Public ID? Here, you go:
\Cloudinary\Uploader::upload('my_image.jpg', array("public_id" => "manutd_id"));
Use the original name of the uploaded file:
You want to preserve and use the original name of the uploaded file? Here, you go:
\Cloudinary\Uploader::upload('sample.jpg', array("use_filename" => TRUE));
Upload an image, video, or a raw file:
Not sure whether a user will upload an image, video, or a raw file? Here, you go:
\Cloudinary\Uploader::upload("spreadsheet.xls", array("resource_type" => "auto"));
Note: See here for all available upload options.
We have successfully covered how to handle file uploads with PHP. Now, the hassle associated with file uploads should be a thing of the past. Storing your files on your host server also should be a thing of the past. Offload files to dedicated cloud storage services like Cloudinary and let them bear the headache of serving the files securely to your web app via CDN!
Prosper Otemuyiwa is a Food Ninja, Open Source Advocate & Self-acclaimed Developer Evangelist. |
cl_image_tag("respimg-guide/pt-2-hero.png", :width=>700, :quality=>"auto", :fetch_format=>:auto, :crop=>"scale")
cl_image_tag("respimg-guide/pt-2-hero.png", array("width"=>700, "quality"=>"auto", "fetch_format"=>"auto", "crop"=>"scale"))
CloudinaryImage("respimg-guide/pt-2-hero.png").image(width=700, quality="auto", fetch_format="auto", crop="scale")
cloudinary.image("respimg-guide/pt-2-hero.png", {width: 700, quality: "auto", fetch_format: "auto", crop: "scale"})
cloudinary.url().transformation(new Transformation().width(700).quality("auto").fetchFormat("auto").crop("scale")).imageTag("respimg-guide/pt-2-hero.png")
$.cloudinary.image("respimg-guide/pt-2-hero.png", {width: 700, quality: "auto", fetch_format: "auto", crop: "scale"})
cloudinary.Api.UrlImgUp.Transform(new Transformation().Width(700).Quality("auto").FetchFormat("auto").Crop("scale")).BuildImageTag("respimg-guide/pt-2-hero.png")
In Part 1 of this series, I discussed (rather abstractly) what it means for an image to be “responsive.” In short, a responsive image is a variable image that adapts to fit variable contexts, in order to provide a great experience to users no matter what their screen, browser, network connection, or device may be.
That adaptive variability can take many forms; the most common and impactful of these is adaptive, variable, image resolution.
Bitmap images have a fixed resolution: they contain a fixed number of pixels. Images on the web, however, are fluid, and the number of pixels that they occupy in layouts and on screens can vary.
Here’s an example. Let’s say we have an image—
—and we want to share it with the world. We put it on a website, and load that site on our phone:
It looks pretty sharp! Proud of our work, we send the URL to a friend, who just so happens to have a brand new 5K display:
He’s, well, less than impressed. The problem? Our original image has a resolution of 800 × 600 — so while it looks sharp-as-a-tack on our 750 × 1334 iPhone 7 screen, it looks horribly blurry when scaled to fit our friend’s 5120 × 2880 monster.
Crispness in this (extreme!) context requires a (much!) bigger image:
resolution | # of pixels | # of bytes |
---|---|---|
800 × 600 | 0.48 MP | 66 kB |
5000 × 3750 | 18.75 MP | 2.2 MB |
To fill our friend’s 5K screen, our image would need almost 40 times more data. This super-sized file would, all by itself, outweigh the average webpage and take forever to load over anything but the fastest internet connections. Worst of all, on smaller (and far more common) screens, our giant image would ultimately be downscaled to fit, and all of those extra pixels would be useless.
So – should we use high-resolution images in order to take advantage of high-resolution hardware? Or should we use small images, which will load fast in low-resolution environments?
The answer, of course, is an emphatic “yes” to both, and everything in-between! We can and should include multiple, alternate versions of our images –
— and make sure that we only send high-resolution versions to users who need them, while still sending low-resolution versions to everyone else.
This concept is simple enough, and the the HTML feature that allows developers to supply those multiple alternates is straightforward, too; it’s called srcset
.
But the devil is in the details, and figuring out—
—is surprisingly tricky! We’ll tackle each of those problems in turn.
In order to serve users in varying contexts well, we need to supply our image in multiple, alternate resolutions. But how many versions do we need, and what should their resolutions be?
This is a hard question, so let’s break it into smaller pieces. We can start by thinking about the limits: what are the largest and smallest resolutions that we’ll need? In order to figure that out, we need to consider: what are the largest and smallest sizes that our image can grow/shrink to on our layout? And what display sizes and densities do we actually care about supporting?
Once we’ve established the limits of our range, we need to figure out how to fill it in.
For web devs, more resources means more to manage.
For users, more resources means less waste. But also, more HTML to download, and fewer cache hits, resulting in, paradoxically, slower loads.
How should we navigate these conflicting priorities? Initially, most developers threw up their hands and picked a more-or-less arbitrary step size, in pixels. Jason Grigsby pioneered the idea that we can be smarter about this by using file-size-based steps, instead. So rather than having versions that were, say, 600, 800, and 1000 pixels-wide, we would generate versions that weighed 60 kB, 80 kB, and 100 kB.
Why? Because different images can have surprisingly different compression characteristics, and we care much more about the wasted bytes when we load an over-sized image, than we do wasted pixels.
Cloudinary thought that this file-size-based “responsive image breakpoint” strategy was pretty smart, and built the Responsive Images Breakpoints Generator in order to help developers implement it. The Generator allows you to input a high-res original image and pick a maximum resolution, minimum resolution, and step-size in kilobytes; it then generates a full set of alternate resources and provides a convenient download link along with the markup you need to include them all in your HTML. Neat!
Now that we’ve marked up a set of multiple, alternate resources – how should browsers pick and load the most appropriate resource out of the set?
In order to choose, browsers need to know how many pixels are in this box:
Once they know that, they can pick the version that best fills the box. But! That box-size is determined by a number of things:
The viewport size and screen density are always known to the browser. Page layout, however, is where things get sticky.
When loading a webpage and parsing its HTML, browsers begin loading external resources (like CSS and images) as soon as they see their URLs in the markup. This is good for performance – image bytes account for 68% of the web, and we want to start moving those bytes across the network as soon as possible. But it’s bad for layout-aware image loading, as it means that browsers don’t, won’t, and can’t wait for layout before kicking off image loads. The load-it-as-soon-as-you-see-it strategy means that we, as web developers, must choose between:
In other words: do we want our images to be resolution-responsive, or loaded without delay? Because – without adding something else to this equation – we can’t have both.
In some contexts, delayed, resolution-adaptive-sizing isn’t a problem. If an image is lazy-loaded (or even just below-the-fold), we can afford to wait for layout. The Cloudinary Javascript library and libraries like Alexander Farkas’ lazy-sizes circumvent the browser’s normal image loading mechanisms and responsively load images – after layout – with Javascript.
But if we want our resolution-responsive images fast, we need to short-circuit the normal flow of information. We need to tell the browser the layout size of the image directly in markup.
This is what the sizes
attribute is for. sizes
declarations have a reputation for being difficult to read and I know from bitter experience that they can be tricky to write. Because the attribute was explicitly designed to break the separation of concerns and duplicate a little bit of layout information right there in markup, the most challenging thing about sizes
might be maintenance; sites whose layout is subject to frequent tweaking will especially suffer.
The reason that I love sizes
despite its difficulties, is that it addresses the how-do-we-know-about-the-layout-before-we-know-about-the-layout problem head on, directly giving browsers the minimum amount of information that they need to start loading resolution-responsive images as soon as possible.
As such, sizes
is required when using the srcset
attribute with w
descriptors – which is the standard (and universally supported!) pattern for implementing resolution-adaptive responsive images.
Fast, resolution-responsive images can be a bit tricky to implement, but the the payoff is enormous. By supplying multiple versions of each image, and using either Javascript or srcset
and sizes
to pick amongst them, we can ensure that every user – whether they’re on a phone, 5K display, or anything in between – gets a version of our image with just enough resolution to look great on their display.
Join us next time, when we’ll talk about another, vital, way that images can vary: variable image encoding.
srcset
, sizes
, and Cloudinary.sizes
, and responsive image breakpoints sections of Jason Grigsby’s 10-part Responsive Images 101 series (already linked above, but down here again, for good measure!)srcset
the server’s problem using w_auto
and Client Hints.cl_image_tag("face_coaster_cover2.jpg")
cl_image_tag("face_coaster_cover2.jpg")
CloudinaryImage("face_coaster_cover2.jpg").image()
cloudinary.image("face_coaster_cover2.jpg")
cloudinary.url().imageTag("face_coaster_cover2.jpg")
$.cloudinary.image("face_coaster_cover2.jpg")
cloudinary.Api.UrlImgUp.BuildImageTag("face_coaster_cover2.jpg")
With the decreasing price-performance ratio of computing, research efforts in face detection and face recognition algorithms are rapidly expanding and new techniques for both of these are achieving greater accuracy and reduced processing time.
Face detection is a technology that identifies whether and where human faces are located in digital images. It’s commonly used in cameras, security applications, graphic applications, and a variety of other web and mobile apps. It is also a prerequisite for face recognition, which uses a database and additional algorithms to match a detected face with a specific individual.
A large percentage of the images in most websites and apps include photos of people -- from profile pics to news articles to group selfies. These photos need to be displayed in sizes, shapes, and styles that match the site design. Performing automatic face cropping or otherwise programmatically modifying images that contain faces necessitates a reliable face detection mechanism. The reliability can be affected by the colors, or lack of colors in the image, the direction a person is facing, the tilt of the head, the size of a face, the complexity of the background, lighting, and more.
Cloudinary provides a complete image and video management solution for web and mobile app programmers. Our solution includes a built-in image face detection functionality and several face-detection features you can take advantage of in your own apps. In most cases, applying this functionality is just a question of adding a parameter or two to your image delivery URLs… and your own creativity, of-course.
Cloudinary recently updated its face detection mechanism. The new mechanism is now very efficient and precise, including many "difficult" scenarios such as side portraits, blurry faces, tricky lighting, and more.
For example, the people in these images are now easily detected despite low lighting, unusual shadows, black and white images, turned heads and closed eyes:
cl_image_tag("dark_portrait_detected.jpg")
cl_image_tag("dark_portrait_detected.jpg")
CloudinaryImage("dark_portrait_detected.jpg").image()
cloudinary.image("dark_portrait_detected.jpg")
cloudinary.url().imageTag("dark_portrait_detected.jpg")
$.cloudinary.image("dark_portrait_detected.jpg")
cloudinary.Api.UrlImgUp.BuildImageTag("dark_portrait_detected.jpg")
cl_image_tag("man-shadow_detected.jpg")
cl_image_tag("man-shadow_detected.jpg")
CloudinaryImage("man-shadow_detected.jpg").image()
cloudinary.image("man-shadow_detected.jpg")
cloudinary.url().imageTag("man-shadow_detected.jpg")
$.cloudinary.image("man-shadow_detected.jpg")
cloudinary.Api.UrlImgUp.BuildImageTag("man-shadow_detected.jpg")
cl_image_tag("side_bw_detected.jpg")
cl_image_tag("side_bw_detected.jpg")
CloudinaryImage("side_bw_detected.jpg").image()
cloudinary.image("side_bw_detected.jpg")
cloudinary.url().imageTag("side_bw_detected.jpg")
$.cloudinary.image("side_bw_detected.jpg")
cloudinary.Api.UrlImgUp.BuildImageTag("side_bw_detected.jpg")
When you deliver an image URL that includes face-detection manipulations, the faces in the original photo are detected on-the-fly, the requested manipulations are performed in the cloud, and then the final image is delivered via CDN.
The rest of this blog will cover some of the many ways that developers can automatically manipulate proprietary and user-generated photos containing faces to fit the needs and design of a website or app.
Perhaps one of the most common uses of face detection is for cropping. A large percentage of images, especially with user-generated content, contain photos of people.
Headshots are needed for profile pictures, chat heads, and more, but the original uploaded photo may include much more than just a face, and that face may not even be the central element in the photo. Group selfies are also popular, and rarely centered, so again, we need to be very careful that even if we crop the photo to a different aspect ratio, we don’t lose any of the crew.
In short, it’s critical in most situations that when we resize and crop uploaded photos, we keep our users’ faces up front and center. There are a variety of features you can use or combine to ensure this.
When cropping photos intended for profile pictures or other headshots, you will usually want to use face
as your cropping gravity
(g_face
in URLs). This ensures that the detected face (or largest face if there is more than one) will be the center of the cropped photo, regardless of its initial location.
If in conjunction with face
gravity, you use thumb
as your crop
method, you will get a crop that’s as tight as possible (given the cropping dimensions) to the detected face coordinates.
You can further adjust your thumbnail cropping by setting a zoom
value, to zoom in or out from the detected face coordinates. And then of-course you can add any other transformations you want.
Here's what the delivery code looks like for that last photo:
cl_image_tag("teen_facedown", :transformation=>[ {:width=>200, :height=>200, :gravity=>"face", :zoom=>0.65, :radius=>"max", :effect=>"improve", :crop=>"thumb"}, {:effect=>"shadow"} ])
cl_image_tag("teen_facedown", array("transformation"=>array( array("width"=>200, "height"=>200, "gravity"=>"face", "zoom"=>0.65, "radius"=>"max", "effect"=>"improve", "crop"=>"thumb"), array("effect"=>"shadow") )))
CloudinaryImage("teen_facedown").image(transformation=[ {"width": 200, "height": 200, "gravity": "face", "zoom": 0.65, "radius": "max", "effect": "improve", "crop": "thumb"}, {"effect": "shadow"} ])
cloudinary.image("teen_facedown", {transformation: [ {width: 200, height: 200, gravity: "face", zoom: 0.65, radius: "max", effect: "improve", crop: "thumb"}, {effect: "shadow"} ]})
cloudinary.url().transformation(new Transformation() .width(200).height(200).gravity("face").zoom(0.65).radius("max").effect("improve").crop("thumb").chain() .effect("shadow")).imageTag("teen_facedown")
$.cloudinary.image("teen_facedown", {transformation: [ {width: 200, height: 200, gravity: "face", zoom: 0.65, radius: "max", effect: "improve", crop: "thumb"}, {effect: "shadow"} ]})
cloudinary.Api.UrlImgUp.Transform(new Transformation() .Width(200).Height(200).Gravity("face").Zoom(0.65).Radius("max").Effect("improve").Crop("thumb").Chain() .Effect("shadow")).BuildImageTag("teen_facedown")
Everything mentioned above works equally well for multiple faces. Just use faces
gravity instead of face
, and your cropping will be based on a rectangle that includes all detected faces.
All of this assumes you can reasonably expect that the uploaded image is going to contain one or multiple faces as the main subject.
Sometimes the main subject of an image isn't necessarily expected to be a person, but you still need to ensure you aren’t cutting off people or other important content of the image. In this case, you can take advantage of the auto
gravity feature. With this automated cropping method, the most prominent elements of the picture determine the cropping coordinates. Unless otherwise specified, any detected faces
get a high priority in this cropping decision, but other prominent elements are also included.
You can learn lots more about auto
gravity in this blog article and this documentation.
There may be situations where you'd like to keep the people in a photograph anonymous. In these cases, you can pixelate or blur all detected faces just by adding a single parameter to your image URL.
In the example below, the faces that Cloudinary detects in the concert audience are generally those that could likely be identified by a human viewer. All of these faces are automatically blurred before the image is delivered, using the blur_faces
effect.
cl_image_tag("concert_crowd", :effect=>"blur_faces:500")
cl_image_tag("concert_crowd", array("effect"=>"blur_faces:500"))
CloudinaryImage("concert_crowd").image(effect="blur_faces:500")
cloudinary.image("concert_crowd", {effect: "blur_faces:500"})
cloudinary.url().transformation(new Transformation().effect("blur_faces:500")).imageTag("concert_crowd")
$.cloudinary.image("concert_crowd", {effect: "blur_faces:500"})
cloudinary.Api.UrlImgUp.Transform(new Transformation().Effect("blur_faces:500")).BuildImageTag("concert_crowd")
And here’s an example of an ice skating scene where a couple of childrens' faces are clearly visible. This time, we use the pixelate option to protect the kids’ identities:
cl_image_tag("ice_skating.jpg", :effect=>"pixelate_faces:4", :width=>500, :crop=>"scale")
cl_image_tag("ice_skating.jpg", array("effect"=>"pixelate_faces:4", "width"=>500, "crop"=>"scale"))
CloudinaryImage("ice_skating.jpg").image(effect="pixelate_faces:4", width=500, crop="scale")
cloudinary.image("ice_skating.jpg", {effect: "pixelate_faces:4", width: 500, crop: "scale"})
cloudinary.url().transformation(new Transformation().effect("pixelate_faces:4").width(500).crop("scale")).imageTag("ice_skating.jpg")
$.cloudinary.image("ice_skating.jpg", {effect: "pixelate_faces:4", width: 500, crop: "scale"})
cloudinary.Api.UrlImgUp.Transform(new Transformation().Effect("pixelate_faces:4").Width(500).Crop("scale")).BuildImageTag("ice_skating.jpg")
Tip: For cases like these, you may want to take advantage of one of the access control options to prevent users from accessing the non-blurred or non-pixelated versions of the image.
There’s no such thing as a face detection mechanism that will detect 100% of the faces 100% of the time, so what if you notice that a particular face you need was not detected? No problem. You can always update the detected coordinates explicitly.
For example, when a user uploads an image to your site, you can retrieve and display the detected face coordinates and then enable your users to adjust the coordinates or add a new face via your UI. You can then pass the updated coordinates to Cloudinary via the Update method of the Admin API.
For your own images, you can also manually change the coordinates using the Media library UI.
Sometimes, just blurring or pixelating a face isn’t enough. Instead, you want to completely cover the face with another image. Or, maybe you just want to add a fun mask. Whatever the reason, it’s simple to add the same image to all faces in a photo.
To do this, you take advantage of the region_relative
flag and faces
gravity when you specify the overlay image. That tells Cloudinary that you want the overlay to be placed on all faces, and that the specified size of each overlay is a percentage relative to the size of each detected face.
For example, here’s a quick way to get your family dressed up in costume:
cl_image_tag("family_portrait.jpg", :gravity=>"faces", :overlay=>"spiderman_mask", :width=>1.1, :flags=>"region_relative")
cl_image_tag("family_portrait.jpg", array("gravity"=>"faces", "overlay"=>"spiderman_mask", "width"=>1.1, "flags"=>"region_relative"))
CloudinaryImage("family_portrait.jpg").image(gravity="faces", overlay="spiderman_mask", width=1.1, flags="region_relative")
cloudinary.image("family_portrait.jpg", {gravity: "faces", overlay: "spiderman_mask", width: 1.1, flags: "region_relative"})
cloudinary.url().transformation(new Transformation().gravity("faces").overlay("spiderman_mask").width(1.1).flags("region_relative")).imageTag("family_portrait.jpg")
$.cloudinary.image("family_portrait.jpg", {gravity: "faces", overlay: "spiderman_mask", width: 1.1, flags: "region_relative"})
cloudinary.Api.UrlImgUp.Transform(new Transformation().Gravity("faces").Overlay("spiderman_mask").Width(1.1).Flags("region_relative")).BuildImageTag("family_portrait.jpg")
You probably already noticed that we gave anonymous (happy) faces to our roller coaster riders at the beginning of this article using the same technique.
And remember, the overlay you use doesn’t have to cover the whole face or even be centered. Below, we add a ranger hat to a man’s head using a relative y- coordinate to offset the location of the hat about 30% above the center of the face.
cl_image_tag("smiling_man", :transformation=>[ {:gravity=>"face", :zoom=>0.6, :crop=>"crop"}, {:overlay=>"ranger_hat", :width=>1.1, :flags=>"region_relative", :gravity=>"face", :y=>-0.3} ])
cl_image_tag("smiling_man", array("transformation"=>array( array("gravity"=>"face", "zoom"=>0.6, "crop"=>"crop"), array("overlay"=>"ranger_hat", "width"=>1.1, "flags"=>"region_relative", "gravity"=>"face", "y"=>-0.3) )))
CloudinaryImage("smiling_man").image(transformation=[ {"gravity": "face", "zoom": 0.6, "crop": "crop"}, {"overlay": "ranger_hat", "width": 1.1, "flags": "region_relative", "gravity": "face", "y": -0.3} ])
cloudinary.image("smiling_man", {transformation: [ {gravity: "face", zoom: 0.6, crop: "crop"}, {overlay: "ranger_hat", width: 1.1, flags: "region_relative", gravity: "face", y: -0.3} ]})
cloudinary.url().transformation(new Transformation() .gravity("face").zoom(0.6).crop("crop").chain() .overlay("ranger_hat").width(1.1).flags("region_relative").gravity("face").y(-0.3)).imageTag("smiling_man")
$.cloudinary.image("smiling_man", {transformation: [ {gravity: "face", zoom: 0.6, crop: "crop"}, {overlay: "ranger_hat", width: 1.1, flags: "region_relative", gravity: "face", y: -0.3} ]})
cloudinary.Api.UrlImgUp.Transform(new Transformation() .Gravity("face").Zoom(0.6).Crop("crop").Chain() .Overlay("ranger_hat").Width(1.1).Flags("region_relative").Gravity("face").Y(-0.3)).BuildImageTag("smiling_man")
If you want to check the face detection status of uploaded images programmatically, you can request to return face coordinate data when you or your users upload images, or you can request them upon demand for already uploaded images using the explicit method of the upload API, or with the list details of a single resource functionality of the Admin API. The following shows an excerpt from the JSON response of an upload call where the faces
parameter was set to true.
{ ... "faces": [ [513, 19, 38, 52], [409, 26, 40, 54], [79, 31, 43, 59], [232, 32, 40, 54], [321, 33, 41, 57], [160, 37, 43, 59] }
For example, you could use this data to calculate the ideal location for a text overlay so that it won’t cover any faces on a photo.
Cloudinary enables you to create programmatically complex manipulations by adding conditions to your transformations. One of the things you can base your conditions on is face_count
.
For example, you can’t always be sure that a profile picture will be of a person. Some people choose to upload family portraits, scenery, or other images to represent themselves. The following images use the identical conditional transformation, where thumbnail cropping is applied if one or more faces is found, but scaled cropping is used on images without faces. We’ve also added a shadow vs black border for the alternative conditions to highlight the differences.
(The code shown here is for the nice_couple
on the left, but you can click the bridge picture to see that it uses the identical transformation URL except for the image name (public ID).)
cl_image_tag("nice_couple", :transformation=>[ {:if=>"fc_gte_1", :gravity=>"faces", :zoom=>0.5, :width=>200, :height=>200, :effect=>"shadow:10", :color=>"#acb2b9", :x=>7, :y=>7, :crop=>"thumb"}, {:if=>"else", :width=>200, :height=>200, :border=>"3px_solid_black", :crop=>"scale"} ])
cl_image_tag("nice_couple", array("transformation"=>array( array("if"=>"fc_gte_1", "gravity"=>"faces", "zoom"=>0.5, "width"=>200, "height"=>200, "effect"=>"shadow:10", "color"=>"#acb2b9", "x"=>7, "y"=>7, "crop"=>"thumb"), array("if"=>"else", "width"=>200, "height"=>200, "border"=>"3px_solid_black", "crop"=>"scale") )))
CloudinaryImage("nice_couple").image(transformation=[ {"if": "fc_gte_1", "gravity": "faces", "zoom": 0.5, "width": 200, "height": 200, "effect": "shadow:10", "color": "#acb2b9", "x": 7, "y": 7, "crop": "thumb"}, {"if": "else", "width": 200, "height": 200, "border": "3px_solid_black", "crop": "scale"} ])
cloudinary.image("nice_couple", {transformation: [ {if: "fc_gte_1", gravity: "faces", zoom: 0.5, width: 200, height: 200, effect: "shadow:10", color: "#acb2b9", x: 7, y: 7, crop: "thumb"}, {if: "else", width: 200, height: 200, border: "3px_solid_black", crop: "scale"} ]})
cloudinary.url().transformation(new Transformation() .if("fc_gte_1").gravity("faces").zoom(0.5).width(200).height(200).effect("shadow:10").color("#acb2b9").x(7).y(7).crop("thumb").chain() .if("else").width(200).height(200).border("3px_solid_black").crop("scale")).imageTag("nice_couple")
$.cloudinary.image("nice_couple", {transformation: [ {if: "fc_gte_1", gravity: "faces", zoom: 0.5, width: 200, height: 200, effect: "shadow:10", color: "#acb2b9", x: 7, y: 7, crop: "thumb"}, {if: "else", width: 200, height: 200, border: "3px_solid_black", crop: "scale"} ]})
cloudinary.Api.UrlImgUp.Transform(new Transformation() .If("fc_gte_1").Gravity("faces").Zoom(0.5).Width(200).Height(200).Effect("shadow:10").Color("#acb2b9").X(7).Y(7).Crop("thumb").Chain() .If("else").Width(200).Height(200).Border("3px_solid_black").Crop("scale")).BuildImageTag("nice_couple")
You can learn more about conditional transformations here.
Just a reminder that in addition to applying all this cool stuff on images that you or your users directly upload, you can also use the same tricks with remotely fetched images, such as Facebook profile pictures.
For example, you can allow your users to select to use their Facebook profile photo as the basis for their account profile photo in your app and then apply resizing, rounding, artistic effects, or any other transformation to fit your site's art direction, while ensuring that the detected face of the fetched photo remains the main focus of the final photo.
Here we apply cropping and several other transformations to the Facebook profile photo from the Remembering JFK page:
cl_image_tag("268587306614095", :type=>"facebook", :transformation=>[ {:width=>200, :height=>200, :gravity=>"face", :zoom=>0.65, :radius=>"max", :effect=>"art:sizzle", :crop=>"thumb"}, {:effect=>"shadow"} ])
cl_image_tag("268587306614095", array("type"=>"facebook", "transformation"=>array( array("width"=>200, "height"=>200, "gravity"=>"face", "zoom"=>0.65, "radius"=>"max", "effect"=>"art:sizzle", "crop"=>"thumb"), array("effect"=>"shadow") )))
CloudinaryImage("268587306614095").image(type="facebook", transformation=[ {"width": 200, "height": 200, "gravity": "face", "zoom": 0.65, "radius": "max", "effect": "art:sizzle", "crop": "thumb"}, {"effect": "shadow"} ])
cloudinary.image("268587306614095", {type: "facebook", transformation: [ {width: 200, height: 200, gravity: "face", zoom: 0.65, radius: "max", effect: "art:sizzle", crop: "thumb"}, {effect: "shadow"} ]})
cloudinary.url().transformation(new Transformation() .width(200).height(200).gravity("face").zoom(0.65).radius("max").effect("art:sizzle").crop("thumb").chain() .effect("shadow")).type("facebook").imageTag("268587306614095")
$.cloudinary.image("268587306614095", {type: "facebook", transformation: [ {width: 200, height: 200, gravity: "face", zoom: 0.65, radius: "max", effect: "art:sizzle", crop: "thumb"}, {effect: "shadow"} ]})
cloudinary.Api.UrlImgUp.Transform(new Transformation() .Width(200).Height(200).Gravity("face").Zoom(0.65).Radius("max").Effect("art:sizzle").Crop("thumb").Chain() .Effect("shadow")).Type("facebook").BuildImageTag("268587306614095")
You can do the same with images fetched from other sites, like Twitter, Google+, or from any image URL.
By now, the many potential uses for face detection have probably become obvious to you, as plain as the nose on your face, you might say…. OK, I'm done with the painful face puns.
The fact is that almost every Website includes both local and user-generated content that centers around people. It’s essential that those people remain the focus of the displayed pictures regardless of how you resize, crop, add images and text overlays, etc.
Cloudinary’s face detection capabilities and updated mechanism enable you to accomplish all of these things programmatically, using simple transformation parameters, and with very reliable results.
Ready to see how far your creativity can take these face detection features? These features are available in all plans including the free plan, with no add-ins required. If you don’t have a Cloudinary account yet, you can sign up for a free one, and give it a go!
Images – if you’re a developer, there’s no doubt that at one time or another, you’ve probably worked with a lot of image files, and may have been tasked with ensuring a top-notch user experience or continually improving website and app performance. You may have posed questions on online message boards, or sought advice from your developer colleagues. But there hasn’t been a conference that solely focused on image management…until now.
Introducing ImageCon – the first-ever event designed to bring together developers for an open, frank discussion of image management challenges. ImageCon, which takes place Wednesday, March 1, at the Four Seasons in San Francisco – will feature presentations by some of today’s visionaries, leading technologists and developers who will share their thought leadership about best practices, including:
Cloudinary is very excited about the great line-up of speakers and the opportunity the event presents for the industry as a whole to advance image management best practices. We believe you’ll come away with practical advice and solutions that you can employ right away to address the challenges you face in optimizing your website for end users.
More importantly, we believe this is a great opportunity to network with your peers and learn from their experiences. Through an open dialogue and sharing real-world experiences, we expect ImageCon to play an important role in advancing – and improving – the practice of image management.
We hope you’ll join us at ImageCon – register today.