netlify-plugin-cloudinary icon indicating copy to clipboard operation
netlify-plugin-cloudinary copied to clipboard

Generate responsive images

Open colbyfayock opened this issue 3 years ago • 37 comments

Cloudinary supports generating responsive images.

https://cloudinary.com/documentation/responsive_images#responsive_breakpoint_generator

cloudinary.uploader.v2.upload("sample.jpg", { responsive_breakpoints: { create_derived: true, bytes_step: 20000, min_width: 200, max_width: 1000, transformation: { crop: 'fill', aspect_ratio: '16:9', gravity: 'auto' } } }, function(error, result) { console.log(result); });

The sizes used should be configurable in the Netlify file-based configuration. Ideally we could also target configuration, for instance, using element selectors to use specific sizes for that specific element(s).

For example: All images get sizes="..." while .my-images get sizes="..."

More Info

  • https://cloudinary.com/blog/introducing_intelligent_responsive_image_breakpoints_solutions
  • https://www.responsivebreakpoints.com/

colbyfayock avatar Sep 27 '22 15:09 colbyfayock

sounds great @Ambareen09!

When using the Cloudinary uploader, we're able to automatically generate responsive images that we can use to improve how images load

That code snippet in the original post is an example of that:

cloudinary.uploader.v2.upload("sample.jpg", {
  responsive_breakpoints: {
    create_derived: true,
    bytes_step: 20000,
    min_width: 200,
    max_width: 1000,
    transformation: {
      crop: 'fill',
      aspect_ratio: '16:9',
      gravity: 'auto'
    }
  }
})

In the response, the breakpoints would be included

here's another simpler example from the docs:

To upload an image from a remote url: https://www.example.com/sample.jpg and request Cloudinary to find the best breakpoints based on the following guidelines: a minimum width of 200 pixels, a maximum width of 1000 pixels, at least 20000 bytes file size difference between the breakpoints, while keeping the generated derived images:

cloudinary.v2.uploader
.upload("https://www.example.com/sample.jpg",
  { responsive_breakpoints: 
    { create_derived: true, 
      bytes_step: 20000, 
      min_width: 200, 
      max_width: 1000 }})
.then(result=>console.log(result));

Under https://cloudinary.com/documentation/image_upload_api_reference#upload_examples

Once we have these breakpoints, we can use them in the code such as:

<img
sizes="(max-width: 1400px) 100vw, 1400px"
srcset="
castle_c_scale,w_200.jpg 200w,
castle_c_scale,w_476.jpg 476w,
castle_c_scale,w_665.jpg 665w,
castle_c_scale,w_816.jpg 816w,
castle_c_scale,w_962.jpg 962w,
castle_c_scale,w_1094.jpg 1094w,
castle_c_scale,w_1215.jpg 1215w,
castle_c_scale,w_1294.jpg 1294w,
castle_c_scale,w_1381.jpg 1381w,
castle_c_scale,w_1400.jpg 1400w"
src="castle_c_scale,w_1400.jpg"
alt="">

we likely need to recognize inputs to turn this on and off. see my comment here for how to do this:

https://github.com/colbyfayock/netlify-plugin-cloudinary/issues/23#issuecomment-1264367515

in an example where someone has an image:

<img src="myimage.jpg" />

we would want the end result to be something along the lines of

<img src="myimage.jpg" sizes="..." srcset="https://res.cloudinary.com/.../myimage.jpg..." />

colbyfayock avatar Oct 04 '22 10:10 colbyfayock

i also encourage you to write a test for your work to help debug and to make sure it's working as expected

colbyfayock avatar Oct 04 '22 10:10 colbyfayock

Hi! Is it okay if I work on this issue? @Ambareen09 are you still working on it? Thank you!

BajajAyush avatar Oct 08 '22 12:10 BajajAyush

It's only been 4 days, we need to wait to hear back from them but if they're not going to work on it it's yours. I'll give them until Tuesday to respond.

colbyfayock avatar Oct 08 '22 13:10 colbyfayock

Thanks for letting us know

colbyfayock avatar Oct 08 '22 14:10 colbyfayock

hey @BajajAyush are you still working on this?

colbyfayock avatar Oct 17 '22 17:10 colbyfayock

Hey @colbyfayock if @BajajAyush is not working on this issue, I'd like to give it a try!

developer-diganta avatar Oct 18 '22 20:10 developer-diganta

@developer-diganta we'll give them until tomorrow to respond, but if they don't you can take it!

colbyfayock avatar Oct 19 '22 01:10 colbyfayock

@developer-diganta are you still interested in this? if so its yours

colbyfayock avatar Oct 21 '22 18:10 colbyfayock

Yes! Gonna start working on it!

developer-diganta avatar Oct 21 '22 20:10 developer-diganta

@colbyfayock I understood that once I get the image from the user, I use cloudinary.uploader.v2.upload to generate responsive breakpoints. Once I get the result, I have a url for example, in the documentation, I saw that eager key has urls for various sizes, I'll then use that in the srcset right? And these transformations should only be applied if the user wants it? The sample response on the site is:

{
  "asset_id": "b5e6d2b39ba3e0869d67141ba7dba6cf",
  "public_id": "eneivicys42bq5f2jpn2",
  "version": 1570979139,
  "version_id": "98f52566f43d8e516a486958a45c1eb9",
  "signature": "abcdefghijklmnopqrstuvwxyz12345",
  "width": 1000,
  "height": 672,
  "format": "jpg",
  "resource_type": "image",
  "created_at": "2017-08-11T12:24:32Z",
  "tags": [],
  "pages": 1,
  "bytes": 350749,
  "type": "upload",
  "etag": "5297bd123ad4ddad723483c176e35f6e",
  "placeholder": false,
  "url": "http://res.cloudinary.com/demo/image/upload/v1570979139/eneivicys42bq5f2jpn2.jpg",
  "secure_url": "https://res.cloudinary.com/demo/image/upload/v1570979139/eneivicys42bq5f2jpn2.jpg",
  "access_mode": "public",
  "original_filename": "sample",
  "eager": [
    { "transformation": "c_pad,h_300,w_400",
      "width": 400,
      "height": 300,
      "url": "http://res.cloudinary.com/demo/image/upload/c_pad,h_300,w_400/v1570979139/eneivicys42bq5f2jpn2.jpg",
      "secure_url": "https://res.cloudinary.com/demo/image/upload/c_pad,h_300,w_400/v1570979139/eneivicys42bq5f2jpn2.jpg" },
    { "transformation": "c_crop,g_north,h_200,w_260",
      "width": 260,
      "height": 200,
      "url": "http://res.cloudinary.com/demo/image/upload/c_crop,g_north,h_200,w_260/v1570979139/eneivicys42bq5f2jpn2.jpg",
      "secure_url": "https://res.cloudinary.com/demo/image/upload/c_crop,g_north,h_200,w_260/v1570979139/eneivicys42bq5f2jpn2.jpg" }]
}

developer-diganta avatar Oct 23 '22 08:10 developer-diganta

looks like you're on the right track. would be great if someone could define the sizes they would like to use in the netlify.toml configuration

colbyfayock avatar Oct 23 '22 21:10 colbyfayock

By sizes you mean the min and max widths?

developer-diganta avatar Oct 24 '22 09:10 developer-diganta

so a few different ways to configure it to the API:

cloudinary.v2.uploader
.upload("https://www.example.com/sample.jpg",
  { responsive_breakpoints: 
    { create_derived: true, 
      bytes_step: 20000, 
      min_width: 200, 
      max_width: 1000 }})

which generates a bunch of steps which may look like:

  [plugins.inputs]
  steps:
    minWidth: 200
    maxWidth: 1000
    createDerived: true
    bytesStep: 20000

Or defining the exact sizes:

cloudinary.v2.uploader
.upload("ftp://user1:[email protected]/sample.jpg", 
  { eager: [
    { width: 400, height: 300, crop: "pad" }, 
    { width: 260, height: 200, crop: "crop", gravity: "north"} ]}) 

which may look like:

  [plugins.inputs]
  sizes
  - width: 400
    height: 300
    crop: pad
  - width: 260
    height: 200
    crop: crop
    gravity: north

colbyfayock avatar Oct 24 '22 15:10 colbyfayock

Getting the idea now! Will start working on it and get back with the doubts😅

developer-diganta avatar Oct 24 '22 17:10 developer-diganta

@colbyfayock https://github.com/colbyfayock/netlify-plugin-cloudinary/blob/main/src/lib/cloudinary.js#L127 is this the uploadOptions where I should specify the breakpoints that is the uploadOptions should have the breakpoints?

developer-diganta avatar Oct 25 '22 20:10 developer-diganta

yup! that should be it

colbyfayock avatar Oct 26 '22 16:10 colbyfayock

@colbyfayock wanted to know if I have to give some default responsive breakpoint values or simply accept it as options from the user?

developer-diganta avatar Oct 26 '22 18:10 developer-diganta

given it's opt-in, i would expect that it requires someone to pass in values in order to work. it would be great to have a simple example in the docs though

colbyfayock avatar Oct 26 '22 19:10 colbyfayock

@colbyfayock could you please tell me how do I read the values from the toml file?

  sizes
  - width: 400
    height: 300
    crop: pad
  - width: 260
    height: 200
    crop: crop
    gravity: north

for this one suppose, how do I read it in the js file? since for uploadOptions, I have to format it in the way the example specifies?

developer-diganta avatar Oct 26 '22 19:10 developer-diganta

@colbyfayock see here for example: https://github.com/colbyfayock/netlify-plugin-cloudinary/blob/main/src/index.js#L25-L37

colbyfayock avatar Oct 27 '22 15:10 colbyfayock

Yup! I saw that. But since I have different values for sizes shall I get an array in return like sizes[0].width gives me 400? Sorry never worked with toml so this confusion!

developer-diganta avatar Oct 27 '22 16:10 developer-diganta

you can try logging the values and running a Netlify build

use the Netlify CLI to test locally https://docs.netlify.com/cli/get-started/

once installed run netlify deploy --build

colbyfayock avatar Oct 27 '22 17:10 colbyfayock

@colbyfayock Thank You for the help. I am able to read the values. But for deliveryType=upload, I am getting this error: image The error message is from this line Is there any value that I am missing?

developer-diganta avatar Oct 29 '22 13:10 developer-diganta

i can't tell from just that unfortunately, i would recommend adding some console logs

do you have your environment variables set up? you need to have the API key and API secret set for uploading, you can find the details on the README

colbyfayock avatar Oct 29 '22 13:10 colbyfayock

Yeah I did that. I have also made an uploadPreset. I am trying to find a solution. If I can't can I make a PR so that you can have a look?

developer-diganta avatar Oct 29 '22 13:10 developer-diganta

you can certainly create a PR! and we can work from there

console.logs didnt help? are you looking through all of the logs, not just the end where the error shows?

add logs before and after the getCloudinaryUrl, simple things like "before" and "after"

add logs inside of getCloudinaryUrl at different points

try to determine where it's failing

colbyfayock avatar Oct 29 '22 13:10 colbyfayock

Yup I tried. I saw that https://github.com/colbyfayock/netlify-plugin-cloudinary/blob/main/src/lib/cloudinary.js#L117 till this line it works. There's something happening after this. Trying to figure it out...

developer-diganta avatar Oct 29 '22 13:10 developer-diganta

try adding a try/catch around these:

    if ( canSignUpload ) {
      // We need an API Key and Secret to use signed uploading

      results = await cloudinary.uploader.upload(fullPath, {
        ...uploadOptions
      });
    } else {
      // If we want to avoid signing our uploads, we don't need our API Key and Secret,
      // however, we need to provide an uploadPreset

      results = await cloudinary.uploader.unsigned_upload(fullPath, uploadPreset, {
        ...uploadOptions
      });
    }

such as:

try {
  results = await cloudinary.uploader.upload(fullPath, {
        ...uploadOptions
      });
} catch(e) {
  console.log(`Failed to upload to Cloudinary: ${e.message}`);
  throw e;
}

colbyfayock avatar Oct 29 '22 13:10 colbyfayock

Thank You! Trying it!

developer-diganta avatar Oct 29 '22 14:10 developer-diganta