Unable to read image from file .SVG (intervention/image)

7.7k views Asked by At

So I'm making an image uploader where I want to make thumbnails but also support svg as well, since GD doesn't support svg types I first tried switching to imagick in the config/image.php file but that didn't change anything.

Which I am uncertain about as it does state it supports it, am I missing a required package that I have to install? If so which one?.

But with that said, when I try to upload an svg image it states:

NotReadableException in Decoder.php line 20:
Unable to read image from file (D:\xampp\htdocs\laravel\public\up/2017/01/07000323-logoSep.svg).

I first tried tackling this with a simple IF structure as I don't really need a thumbnail for SVG images, by using ->mime() but then it just said the image couldn't be opened/read either.

$image = $request->file('file');
$imageName = date("dHis-").$image->getClientOriginalName();
$uploadPath = public_path('up/').date("Y/m");
$image->move($uploadPath,$imageName);
$imageMime = Image::make($uploadPath.'/'.$imageName);
if($imageMime->mime() != "image/svg+xml"){}

With that I first thought this was caused by a permission issue so I made sure all my files are read and writable, but that didn't change the issue.

So I tried to base myself on the actual extension rather than the mime type which does somewhat work in my case as the following:

public function dropzoneStore(Request $request){
    $image = $request->file('file');
    $imageName = date("dHis-").$image->getClientOriginalName();
    $uploadPath = public_path('up/').date("Y/m");
    $image->move($uploadPath,$imageName);
    if($image->getClientOriginalExtension() != 'svg'){
        $imageThmb = Image::make($uploadPath.'/'.$imageName);
        $imageThmb->fit(300,300,function($constraint){$constraint->upsize();})->save($uploadPath.'/thm_'.$imageName,80);
    }
    return response()->json(['success'=>$imageName]);
}

But I find this to be a rather hackish approach. Isn't there a better way to filter out or support svg types with the whole intervention/image package?

Thanks in advance for further information!

2

There are 2 answers

0
Rafaël De Jongh On BEST ANSWER

So with further experimentation and trying to get something to work with staying to the intervention/image library but without converting the svg to anything I've gone with the following solution:

public function dropzoneStore(Request $request){
    $image = $request->file('file');
    $imageName = date("dHis-").preg_replace("/[^a-zA-Z0-9.]/","",$image->getClientOriginalName());
    $uploadPath = public_path('up/').date("Y/m");
    $image->move($uploadPath,$imageName);
    //Thumbnail Creation
    $thumbPath = $uploadPath.'/thumbs/';
    File::isDirectory($thumbPath) or File::makeDirectory($thumbPath,0775,true,true);
    if($image->getClientOriginalExtension() != 'svg'){
        $imageThmb = Image::make($uploadPath.'/'.$imageName);
        $imageThmb->fit(300,300,function($constraint){$constraint->upsize();})->save($uploadPath.'/thumbs/thm_'.$imageName,80);
    }else{
        File::copy($uploadPath.'/'.$imageName,$uploadPath.'/thumbs/thm_'.$imageName);
    }
    return response()->json(['success'=>$imageName]);
}

Which while a bit far fetched and a hacky approach in my eyes, still does seem to do the trick to work alongside with my file system that requires a thumb for every image.

While I might look into it anyway when I further expand the use of my website to eventually do convert the SVG images to thumbnails. But for now this will do and as .svg's aren't that used yet for website development I can be at ease as well in terms of load.

Either way I thank everyone who tried to assist me with this matter!

0
Steve Woodson On

For anyone coming across this that wants to force Intervention Image to ignore SVGs specifically for URL Manipulation, I came up with a solution that works pretty well (specific to Laravel but should be usable for other implementations too). I documented it at https://stevenwoodson.com/blog/making-intervention-image-ignore-svgs/ but the gist is the following addition to the render method in the app/Exceptions/Handler.php file:

    /**
     * Render an exception into an HTTP response.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Throwable   $exception
     * @return \Illuminate\Http\Response
     */
    public function render($request, Throwable $exception)
    {
        /**
         * If the error class is `Intervention\Image\Exception\NotReadableException`, 
         * redirect the image URL to the original instead to avoid 500 errors on
         * the frontend. In particular, it has trouble with SVG images as the GD 
         * Library doesn't support anything other than JPG, PNG, GIF, BMP or WebP files.
         * 
         * This RegEx handles all Intervention Image filters defined ('/small/',
         * '/medium/', '/large/', for example) by isolating the URL path after
         * the configured Intervention URL Manipulation route (`imagecache` in 
         * this case) and replaces it with `/original/` which is a built in 
         * Intervention route that sends am HTTP response with the original image file.
         * 
         * @see https://image.intervention.io/v2/usage/url-manipulation
         */
        if ( get_class($exception) == "Intervention\Image\Exception\NotReadableException" ) {
            header('Location: '. preg_replace('/(imagecache\/[^\/]*\/)+/i', 'imagecache/original/', $request->getUri()) );
            exit();
        }

        return parent::render($request, $exception);
    }

The whole description of what this is doing is in the comment block, be sure to change imagecache in there to whatever you defined as your route in config/imagecache.php!