ImageMagick Articles

Install and Configure ImageMagick for PHP with brew package manager

0 👍
👎 0
 PHP
 ImageMagick

This tutorial will show you step-by-step how to use brew package manager to install ImageMagick and then configure it to work with PHP. 

First check if ImageMagick already exists.  Either command below should do that for you.

 brew info imagemagick


If ImageMagick isn't installed you can simply use the command below to install it.

 brew install imagemagick


Now we need to setup PHP to use the ImageMagick extension.  To do so we can use the pecl package manager.


Check if pkg-config is installed.

 brew info pkg-config

 

If pkg-config isn’t installed run the following command.

 

brew install pkg-config

 

Set a few configurations for pecl.  Run the following 3 commands.

 export PKG_CONFIG_PATH="$(brew --prefix imagemagick)/lib/pkgconfig"

 export CPPFLAGS="-I$(brew --prefix imagemagick)/include/ImageMagick-7"

 export CFLAGS="-DMAGICKCORE_HDRI_ENABLE=1 -DMAGICKCORE_QUANTUM_DEPTH=16"


Install the pecl imagick extension.

 pecl install imagick


Restart PHP with brew.

 brew services restart php


Check that gd and imagick extensions are set up properly.

 php -m | grep -E "gd|imagick"


And that’s it!  You may run into an issue with a non-existing imagick extension.  If you have multiple versions of PHP installed and frequently switch between different versions you will need to install the Imagick extension for each PHP version.

Upload and Resize an Image with Laravel Vue and ImageMagick

0 👍
👎 0
 Laravel
 VueJS
 PHP
 ImageMagick
 Bootstrap

In this example I use the Laravel and Vue frameworks to implement a SPA tool to upload images and display a list of images that have been uploaded.  The application also demonstrates image resizing with the amazing PHP ImageMagick library.  So there are a few prerequisits to get started however I won't cover that in this tutorial.  I've added some links to get you through the prerequisits:

  1. Install imagemagick and the pecl imagemagick package. Configure the extension in php.
  2. Install and set-up Laravel to use the Vue 2 Options API with Webpack compiler.
  3. Create required Controllers, Blade Views, routes(web.php, api.php, routes.js) and Vue components

Assuming you have an initial setup you will need to generate an API controller to handle requests made from the Vue components.  You can generate the controller with php artisan:

php artisan make:controller Api/ImageController

Next, we need to specify a directory to store these images.  For this tutorial we will upload our images to /public/images.  So notice in the ImageController the method public_path('images/'.$imageName) is used to specify this location.

 

/app/Http/Controllers/Api/ImageController.php

We need two methods to handle the functionality for this application.  An index method for GET requests to retrieve a list of all images.  And an upload method to handle a POST request to upload an image.

 

<?php

namespace App\Http\Controllers;

 

use Illuminate\Http\Request;

use Illuminate\Support\Facades\File;

use Imagick;

 

class ImageController extends Controller

{

   /**

    * index

    */

   public function index()

   {

       $fileNames = [];

       $imagesPath = public_path('images/');

       $files = File::files($imagesPath);

       foreach ($files as $key=>$file) {

           $fileNames[] = [

               'file'=>$file->getFilename(),

               'path'=>$file->getPathname(),

               'size'=>$file->getSize(),

               'ext'=>$file->getExtension()

           ];

       }

       return response()->json($fileNames);

   }

 

 

   /**

    * upload

    */

   public function upload(Request $request)

   {

       $request->validate([

           'image' => 'required|image|mimes:jpeg,png,jpg,gif,svg|max:2048',

       ]);

 

       // Create a new Imagick object

       $imagick = new Imagick();

 

       // Read the uploaded image

       $imagick->readImage($request->file('image')->getPathname());

 

       // Resample the image (change the resolution)

       $imagick->resizeImage(100, 100, Imagick::FILTER_LANCZOS, 1);

 

       // Save the image

       $imageName = time() . '.' . $request->file('image')->getClientOriginalExtension();

       $imagick->writeImage(public_path('images/' . $imageName));

 

       // Clear the Imagick object

       $imagick->clear();

       $imagick->destroy();

 

       $fileNames = [];

       $imagesPath = public_path('images/');

       $files = File::files($imagesPath);

       foreach ($files as $file) {

           $fileNames[] = [

               'file'=>$file->getFilename(),

               'path'=>$file->getPathname(),

               'bytes'=>$file->getSize(),

               'ext'=>$file->getExtension()

           ];

       }

      

       return response()->json($fileNames);

   }
}

 

/resources/js/Image/Upload.vue

The Upload.vue component will display an image upload form and a script to interact with the form actions.  Once the user selects an image, they will be able to preview the image before upload.  Notice the API request made with Axios POST to /api/images.  These routes are specified in /routes/api.php

<template>

   <div class="container">

       <div class="row justify-content-center">

           <div class="col-md-8">

               Upload Image Form

                   <div class="row m-1 g-1">

                       <div class="form-group col-md-12">

                           <input class="form-control" :class="[{'text-danger': 'image' in errors && errors.image.length > 0}]" type="file" @change="onFileChange" name="image" id="image"  accept="image/*" >

                       </div>

                       <div v-if="'image' in errors" class="text-danger">                

                           <span v-for="(error, index) in errors['image']" :key="'type-'+index">

                               {{error}}

                           </span>

                       </div>

                   </div>

                   <div v-if="preview" class="mt-3">

                       <img :src="preview" alt="preview" width="100" height="100" />

                   </div>

                   <div class="row m-1 g-1">

                       <div class="form-group col-12">

                           <button type="button" @click.prevent="cancelUpload" class="btn btn-sm btn-fw btn-danger float-end mx-2">

                               Cancel

                           </button>

                           <button class="btn btn-sm btn-fw btn-primary float-end" type="submit" @click.prevent="uploadImage">Upload Image</button>

                       </div>

                   </div>

           </div>

       </div>

   </div>

</template>

<script>
export default {

 

   data() {

       return {

           image: null,

           preview: null,

           user: {},

           errors:{

               image: []

           },

       };

   },

 

   methods: {

       cancelUpload(){

           let redirectTo = this.$route.query.redirect || { name: 'admin-images' };

           this.$router.push(redirectTo);

       },

       async uploadImage(){

           if(this.image)

           {

               let fd = new FormData();

               fd.append('image', this.image);

               try {

                   let resp = (await axios.post('/api/images', fd)).data.data;

                   let redirectTo = this.$route.query.redirect || { name: 'admin-images' };

                   this.$router.push(redirectTo);

               } catch(error) {

                   this.errors = error;

               }

           }

           else {

               this.errors.image.push(`An image file is required for upload.`);

           }

          

       },

       onFileChange(e) {

           let file = e.target.files[0];

           if (file) {

               this.image = file;

               this.preview = URL.createObjectURL(file);

           }

       }

   },

   created() {

       console.log('Image/Upload COMPONENT CREATED.');       

   }
}
</script>

 

/resources/admin/js/Image/Index.vue

The Index.vue component will display a list of images that currently exist in the /public/images directory.  Also notice the API GET request made to /api/images.  

<template>

   <div class="container">

       <div class="row justify-content-center">

           <div class="col-md-10">

               <router-link

                   class="btn btn-primary mb-2 float-right"

                   :to="{ name: 'image-upload', query: {redirect: $route.fullPath} }">

                Image Upload

               </router-link>

 

               <div class="mb-4 row" v-for="(image, index) in images" :key="index">

                <img

                       :src="`/images/${image.file}`"

                       class="img-thumbnail "

                       width="100"

                     />

               </div>

           </div>

       </div>

   </div>

</template>

<script>

export default {

   data() {

       return {

           images: [],

           user: {}

       };

   },

   methods: {

   },

   async created() {

       let response = await axios.get("/api/images");

       this.images = response.data.length ? response.data : [];

   }

};

</script>

 

/resources/js/routes.js

In order to see the Image/Index.vue and Image/Upload.vue components we need to specify a route for each in routes.js.  Because the path /images and /image/upload routes are handled via Vue we need to use a <router-link> inside of another Vue component order to navigate to /images and /image/upload.  

<router-link class="nav-link active" :to="{name: 'image-index'}">Images</router-link>
<router-link class="nav-link active" :to="{name: 'image-upload'}">Upload</router-link>

Then configure the routes in /resources/js/routes.js like below:

 

 import ImageIndex from "./Image/Index";

 import ImageUpload from "./Image/Upload";

 

 const routes = [

       // add these 2 new routes

{

    path: "/images",

    component: ImageIndex,

    name: "image-index",

},

{

    path: "/image/upload",

    component: ImageUpload,

    name: "image-upload",

}

 ];

 

 

/routes/api.php

Finally we create API routes to upload and retrieve images.  Notice both routes both go to /images and either Route::get or Route::post will be used to identify them properly in a request.

 

// GET request to ‘/api/images’

Route::get('/images', [App\Http\Controllers\Api\ImageController::class, 'index']);

 

// POST Request to ‘/api/images’
Route::post('/images', [App\Http\Controllers\Api\ImageController::class, 'upload']);