Uploading Media in Spring Boot Programmatically With Cloudinary

Uploading Media in Spring Boot Programmatically With Cloudinary

Developers building software with media features must store the media somewhere and, in most cases, transform and optimize it for the best experience possible. All the tasks involved they can perform with Cloudinary's end-to-end image- and video management solution.

However, while building a Spring Boot REST API a while ago, I found that integrating with Cloudinary was a struggle, for it took a lot of time combing through GitHub repositories and debugging errors. In addition, although the Cloudinary documentation is pretty good, it does not describe the workflow for Spring Boot, and I had to figure that out on my own.

This tutorial walks you through the process of programmatically uploading media to Cloudinary from a Spring Boot application, saving you the time and effort I had to put in to get my code to work. For a sample reference, see my Spring Boot API on GitHub).

Acquiring the Prerequisites

You must have the following expertise:

  • A basic understanding of the RESTful API design
  • Working knowledge of Spring Boot and Java 8

In addition, have at hand a Java IDE.

Setting Up a Cloudinary Account

First, if you don’t have one yet, sign up for a free Cloudinary account, which offers you credits for storage, transformations, and bandwidth. Alternatively, upgrade to a paid subscription for more credits and other features.

Enabling Unsigned Uploads

Next, enable unsigned uploads in your Cloudinary account’s default upload preset, after which you can upload media into the account’s Media Library without presigning upload requests.

Take the steps below to set up an upload preset, i.e., centrally configure your upload options, eliminating the need for specifying the configurations for each and every upload, and then enable the preset.

  1. On your Cloudinary account’s Dashboard, click Settings (the icon in the form of a gear at the top) and then the Upload tab, also at the top. Scroll down to the Upload presets section.
  2. Click Edit next to my-unsigned preset and, in the next screen, set Signing Mode to Unsigned.

Screenshot from 2022-01-05 01-39-58.png

Integrating Cloudinary Into a Spring Boot Application

The easiest way to integrate Cloudinary into a Spring Boot application is through Cloudinary’s Maven dependency. Follow the steps below.

  1. In your application’s pom.xml file, add the Cloudinary dependency to the list of dependencies:

     <dependency>
         <groupId>com.cloudinary</groupId>
         <artifactId>cloudinary-http44</artifactId>
         <version>1.17.0</version>
     </dependency>
    
  1. Create a bean method with the @bean method-level annotation, which will return your Cloudinary configuration. That way, you can apply the dependency configured in step 1 across your entire application.

In the main Application.java class of your application, add the code snippet below. Be sure to replace the variables cloud_name, api_key, and api_secret with their values, which are displayed on your Cloudinary account’s Dashboard.

    @Bean
    public Cloudinary cloudinaryConfig() {
        Cloudinary cloudinary = null;
        Map config = new HashMap();
        config.put("cloud_name", cloudName);
        config.put("api_key", apiKey);
        config.put("api_secret", apiSecret);
        cloudinary = new Cloudinary(config);
        return cloudinary;
    }

You can now use Cloudinary anywhere in your Spring Boot application.

Uploading Images

The code snippets below from the Spring Boot application referenced above enable you to upload images to Cloudinary with a top-down approach: RestController -> Service -> Application Properties. Ignore the lines that are commented out; they’re not relevant for this tutorial.

RestController

private final CloudinaryGifService cloudinaryGifService;
private final UserService userService;

@PostMapping("/gifs")
public ResponseEntity<LinkedHashMap<String, Object>> uploadGif(@RequestParam("gifFile") 
MultipartFile gifFile, Authentication authentication, @RequestParam("title") String title) throws IOException {
    // User currentUser = 
// userService.findUserByEmail(authentication.getName()); // Authorization
    String url = cloudinaryGifService.uploadFile(gifFile);
    cloudinaryGifService.saveGifToDB(url, title , currentUser);


    // LinkedHashMap<String, Object> jsonResponse = cloudinaryGifService.modifyJsonResponse("create", URL);
    return new ResponseEntity<>(jsonResponse,HttpStatus.CREATED);
}

In the above code:

  • The cloudinaryGifService variable instantiates an object of the CloudinaryService class, which holds the logic that converts the uploaded MultipartFile file to a regular file before uploading it to Cloudinary.
  • The uploadGif method accepts the gif title and gif image as a MultipartFile file.
  • The Service class uploads the image to your Cloudinary account and then saves the image (gif) URL and title to the database of the application.

{note}

gif is specified in the code because the TeamWork API handles only images of the format type GIF (.gif). In actual fact, this process works for image files of other formats, such as .png, .jpeg, etc.

{/note}

Service

Recall that the CloudinaryService class contains the logic for converting and uploading images to Cloudinary. Here is the code in question:

private final Cloudinary cloudinaryConfig;

public String uploadFile(MultipartFile gif) {
    try {
        File uploadedFile = convertMultiPartToFile(gif);
        Map uploadResult = cloudinaryConfig.uploader().upload(uploadedFile, ObjectUtils.emptyMap());
        boolean isDeleted = uploadedFile.delete();

        if (isDeleted){
           System.out.println("File successfully deleted");
        }else
            System.out.println("File doesn't exist");
        return  uploadResult.get("url").toString();
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

private File convertMultiPartToFile(MultipartFile file) throws IOException {
    File convFile = new File(file.getOriginalFilename());
    FileOutputStream fos = new FileOutputStream(convFile);
    fos.write(file.getBytes());
    fos.close();
    return convFile;
}

In the above code:

  • cloudinaryConfig instantiates your Cloudinary configuration from the @bean method you created earlier.
  • uploadFile converts MultipartFile to a normal file format with the convertMultiPartToFile method because interface MultipartFile represents an uploaded file received in a multipart request. Afterwards, uploadFile uploads the file to Cloudinary and returns the GIF image’s URL for the RestController uploadFile method.
  • The boolean isDeleted checks if the file was deleted after upload to ensure that no images are stored in your Spring Boot application’s file system.

To test the uploads with POSTMAN, follow the steps in the answers on this Stack Overflow page.

To upload images up to 10 MB in size, increase your application’s Spring servlet’s multipart file-size by adding this code to your application.properties file:

spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB

Video Upload

To upload videos programmatically with Cloudinary, follow the same process for uploading images. Note that Cloudinary’s upload method uploads files up to 100 MB only. For videos of a larger size, specify the upload_large method, which uploads large files to Cloudinary in chunks:

cloudinary.uploader().uploadLarge("my_large_video.mp4", ObjectUtils.asMap("resource_type", "video"));

Be sure to update the CloudinaryService class and the uploadFile method, like this:

public String uploadFile(MultipartFile gif) {
    try {
        File uploadedFile = convertMultiPartToFile(gif);
        Map uploadResult = cloudinaryConfig.uploader().uploadLarge(uploadedFile, ObjectUtils.emptyMap());

        boolean isDeleted = uploadedFile.delete();` \

        if (isDeleted){
           System.out.println("File successfully deleted");
        }else
            System.out.println("File doesn't exist");
        return  uploadResult.get("url").toString();
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

Also, raise the limit of spring.servlet.multipart.max-request-size in your application.properties file.

References

For further reference, check out the following: