Possible to generate images from frontend with URL?

Hi there.
I’m trying to build a Vue based listing page for a portfolio type situation. It’ll display a grid of thumbnail links, along with some search / filter / sort functionality.

For each thumbnail img I’m looking to build a srcset component. Ideally I’d like to send the default thumbnail img url, and have my Vue component ‘ask’ for the various size variations needed by adding parameters to the url / filename.
eg. for image:
http://my-New-Site/media/pages/projects/a-Sub-Page/###-###/thumbnail-image.jpg
Can I get a lower resolution / quality version by asking for something like:
http://my-New-Site/media/pages/projects/a-Sub-Page/###-###/thumbnail-image-200x200-q10.jpg

I get the sense this may be possible with the api, but I can’t figure out how to go about it.
I have a backup option of sending all the urls required within the json request, but if this is possible it would make my life a lot easier…

Thanks!

The images need to exist first i think, otherwise your Vue component wont get anything… how ever you could probably use the router to trigger the generation, if you craft the url in a way to trigger it.

You could maybe call the images but put the word “generate” in the URL, then you just need to pick up on the the file name at the end of the URL and pass it through the srcset helper. I think there are helpers for getting parts of the URL now, so that should not be too hard.

1 Like

Thanks for the pointers; it was a good call.
I decided the router was probably the best way to go.

This is what I’ve come up with:

'routes' => [
	[
		'pattern' => '(:all)/thumbgen/(:num)/(:num)',
		'action'  => function ($path, $width, $height) {
				
			// Get width & height
			$width = intval( $width );
			$height = intval( $height );
			
			// Split URL into array
			$urlArray = Str::split($path,'/');
			// Get rid of redundant 'pages' from start of path
			$goAway = array_shift( $urlArray );
			// Then get img name
			$img = array_pop( $urlArray );
			// Then join whats left to get page name
			$name = A::join( $urlArray, '/' );

			return page($name)->file($img)->thumb(['crop'=>true,'width'=>$width,'height'=>$height]);
		}
	]
]

Based on a url such as

Some-web-location/pages/projects/the-page-name/an-Image.jpg/thumbgen/300/175

I can get it to return the image (media folder version), but it won’t process the thumbnail.
…whoops…
I’ve just realised this is because I’m confusing file() with $file-> .
Can anyone help me with this last step?

I think it’s because you’re working with a FileVersion object, which doesn’t work for further image manipulations.

Possibly related to https://github.com/getkirby/ideas/issues/242. If you’ld like this to be possible, please upvote the issue (click the thumbsup-emoji in the left bottom).

Edit: your code actually looks ok. Maybe you should just add ->url() in your return:
return page($name)->file($img)->thumb(['crop'=>true,'width'=>$width,'height'=>$height])->url();?

Does it not even create the job for the thumbnail in the media folder?

But I agree with @bvdputte that you should return the URL to the generated file and make sure that the image is not a thumbnail already.

The pages bit in the URL is superfluous, you might as well remove that from the pattern you’re calling (then you don’t have to remove it again from $path.

Sorry for delay getting back to you. Yes, it did generate a job in media (I hadn’t realised what you meant initially). It took me fair while to understand what that meant though.

I finally managed to get this to work though, using the following:

'routes' => [
   [
        'pattern' => 'pages/(:all)/gen/(:num)/(:num)',
	    'action'  => function ($path, $width, $height) {
				
			// Get width & height
			$width = intval( $width );
			$height = intval( $height );
			
			// Split URL into array
			$urlArray = Str::split($path,'/');
			// Then get img name
			$img = array_pop( $urlArray );
			// Then join whats left to get page name
			$name = A::join( $urlArray, '/' );
			
			$output = page($name)->file($img)->thumb(['crop'=>true,'width'=>$width,'height'=>$height]);

			go( $output->url() );
		}
	]
]

Thanks again for all the help everyone.

I’d recommend you check if both the page and the image exist before calling the file/thumb method:


if (($p = page($name)) && $image = $p->file($img)) {
  $output = $image->thumb(['crop'=>true,'width'=>$width,'height'=>$height]);
  go($output->url());
}

Yes good call!
Thanks.

As per @texnixe edit:

'routes' => [
	[
		'pattern' => 'pages/(:all)/gen/(:num)/(:num)',
		'action'  => function ($path, $width, $height) {
				
			// Get width & height
			$width = intval( $width );
			$height = intval( $height );
			
			// Split URL into array
			$urlArray = Str::split($path,'/');
			// Then get img name
			$img = array_pop( $urlArray );
			// Then join whats left to get page name
			$name = A::join( $urlArray, '/' );
			
			if (($p = page($name)) && $image = $p->file($img)) {
				$output = $image->thumb(['crop'=>true,'width'=>$width,'height'=>$height]);
				go($output->url());
			}
		}
	]
]

I also have a recommendation: you have to be aware that if you create public URL-based API’s like this it can be a vector to bring your site down by requesting lots of different dimensions for the same images (as such hammering your webserver with costly requests).

It’s probably better to define some presets (predefined width/height) that can be called?

1 Like