How to upload Image in Mojolicious

Posted by Ashutosh on April 19, 2020



Hello Mojo learners, in this article we learn, how to upload image to the web server using Mojolicious framework.

In every or in most of the web applications, it is required to upload the image to the web server. Be it a user profile image or blog cover image or the home page etc.

PreRequisites

Before proceeding, there are some prerequisites:

  1. Perl elementary knowledge required.
  2. Understanding of Mojolicious
  3. Mojolicious

If you are not familiar with anyone of the above, then I recommend to get required elementary knowledge.

We already cover how to install and setup the Mojolicious Project here.

Hold on for a moment, and Let's think about the steps that we need to follow to upload he image:

  1. We need to register a route first to open the image upload form.
  2. We need to create a Controller to accommodate the above route.
  3. Image upload form template.
  4. We need to create a Controller to post and save the image to the server.

Go to the MyApp.pm and in the startup subroutine register the route under the the startup subroutine.

$r->get('/upload_image')->to(
  controller => 'UploadImageController', action     => 'index'
);

It says, when a user hits upload_image route, it goes to the UPloadImageController and looks out for the index subroutine. At present, we don't have the UploadImageController yet. Create it under the Controller folder.

package MyApp::Controller::UploadImageController;
use Mojo::Base 'Mojolicious::Controller';

use strict;
use warnings;

# This subroutine is to show the upload_image route.
sub index {
  my $c = shift;

  $c->render( template => 'uploadImage');
}

1;

In the subroutine, $c is the default variable and we render the template under the templates folder. Repeatedly we have not created the template yet. Before creating uploadImage.html.ep file go to the default.html.ep under the layouts and add bootstrap 4 files in it.

<!DOCTYPE html>
<html>

<head>
    <title>Upload Image</title>

    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous" />

</head>

<body>
    <%= content %>

    <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"
        integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n"
        crossorigin="anonymous">
    </script>
    
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"
        integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo"
        crossorigin="anonymous">
    </script>
    
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"
        integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6"
        crossorigin="anonymous">
    </script>
</body>

</html>

Now, create uploadImage.html.ep file

% layout 'default';
<br />
<br />

<div class="col-md-6 mx-auto">
    <form method="post" action="/upload_image">
        
      	<div class="input-group">
            
          	<div class="custom-file">
                
              	<input type="file" 
                       class="custom-file-input" 
                       name="upload_image" 
                       id="upload_image" 
                       accept="image/x-png,image/gif,image/jpeg, image/jpg"
                       type='file'
                       required 
                />
            
              	<label class="custom-file-label" for="upload_image">Select Image</label>
            
          	</div>
            
          <div class="input-group-append">
            
            	<input class="btn btn-primary" type="submit" value="Upload" />
        
          </div>
        
      	</div>

  	</form>

</div>

Visit the browser http://localhost:3000/upload_image

upload_image

So far, In the template, we created a form to upload the image. But we have not registered the route to upload the image.

In the MyApp.pm file, under the startup sub routine,

$r->post('/upload_image')->to(
    controller => 'UploadImageController',
    action     => 'upload_image'
);

Create an action upload_image under UploadImageController

At the starting of the file add two constants

use constant ROOT      => 'YOUR_PROJECT_PATH';
use constant IMAGE_LOC => 'YOUR_IMAGE_LOC';

 

# This subroutine is to upload the image to the web app.
sub upload_image {
    my $c = shift;

    my ( $image, $image_file );

    if ( !$c->param('upload_image') ) {
        $c->flash( error => 'Image is required.' );
        $c->redirect_to('/upload_image');
    }

    # Check for Valid Image Extension
    if ( $c->param('upload_image')->filename !~ /.png$|.jpg$|.jpeg$/ ) {
        $c->flash( error => 'Invalid Image Extension.' );
        $c->redirect_to('/upload_image');
    }

		# Upload the image
    $image = $c->req->upload('upload_image');

    $image_file = ROOT . IMAGE_LOC . $c->param('upload_image')->filename;

    $image->move_to($image_file);

    $c->flash( message => 'Image Uploaded successfully.' );
    $c->redirect_to('/upload_image');

}

Complete UploadImageController, looks like

package MyApp::Controller::UploadImageController;
use Mojo::Base 'Mojolicious::Controller';

use strict;
use warnings;

use constant ROOT      => 'PROJECT_PATH';
use constant IMAGE_LOC => 'IMAGE_LOC';

sub index {
    my $c = shift;

    $c->stash( error   => $c->flash('error') );
    $c->stash( message => $c->flash('message') );

    $c->render( template => 'uploadImage' );
}

# This subroutine is to upload the image to the web app.
# As far as I concerned it looks good to me.
sub upload_image {
    my $c = shift;

    my ( $image, $image_file );

    if ( !$c->param('upload_image') ) {
        $c->flash( error => 'Image is required.' );
        $c->redirect_to('/upload_image');
    }

    # Check for Valid Image Extension
    if ( $c->param('upload_image')->filename !~ /.png$|.jpg$|.jpeg$/ ) {
        $c->flash( error => 'Invalid Image Extension.' );
        $c->redirect_to('/upload_image');
    }

		# Upoload the image
    $image = $c->req->upload('upload_image');

    $image_file = ROOT . IMAGE_LOC . $c->param('upload_image')->filename;

    $image->move_to($image_file);

    $c->flash( message => 'Image Uploaded successfully.' );
    $c->redirect_to('/upload_image');

}

1;

Open the uploadimage template and add at the last of the file

<br>
% if ( $error ) {
    <div class="alert alert-danger">
        <p> <%= $error %> </p>
    </div>
% }

% if ( $message ) {
    <div class="alert alert-success col-md-6 mx-auto">
        <p> <%= $message %> </p>
    </div>
% }

 

Your complete html file looks like

% layout 'default';

<br />
<br />

<div class="col-md-6 mx-auto">
    <form method="post" action="/upload_image" enctype ="multipart/form-data">
        <div class="input-group">
            <div class="custom-file">
                <input type="file" 
                       name="upload_image" 
                       id="upload_image" 
                       accept="image/x-png,image/gif,image/jpeg"
                       required
                />
                <label class="custom-file-label" for="upload_image">Select Image</label>
            </div>
            <div class="input-group-append">
                <input class="btn btn-primary" type="submit" value="Upload" />
            </div>
        </div>
    </form>
</div>
<br>
% if ( $error ) {
    <div class="alert alert-danger">
        <p> <%= $error %> </p>
    </div>
% }

% if ( $message ) {
    <div class="alert alert-success col-md-6 mx-auto">
        <p> <%= $message %> </p>
    </div>
% }

 

Now go to the browser, and select the image, and you'll see similar to this.

That's all for now. In this article, we see how to upload the image using the Mojolicious framework. This code has one issue, it doesn't check the size of the image uploaded. I'll leave to the readers to figure it out.

Hint: $c->param('upload_image')->size