5.16.2

Generate and Upload Scaled Images

Overview

The scaling feature allows your server to breathe a bit easier as image scaling efforts are handled client-side by Fine Uploader. If your users submit any image files that can be rendered natively by the current browser, Fine Uploader will create scaled versions and upload those as well. You can tell Fine Uploader which sizes you would like it to generate and upload. You can specify size-specific text to append to each scaled image name. The scaled image can be correctly oriented by Fine Uploader before it is sent to your server. You can also ensure only scaled images, and not the original image, are uploaded. Finally, you can ask Fine Uploader to magically insert the EXIF data from the original image into each scaled image.

Generating and uploading scaled versions of each submitted image can be as simple as specifying an array of objects with name and maxSize properties to indicate the scaled image file's name and maximum size, respectively.

Basic Operation

By default, Fine Uploader will send the original image file, along with any scaled versions you have requested via the scaling.sizes option. Also, by default, all scaled versions will be correctly oriented before they are uploaded.

You must provide a sizes array though. For example:

var uploader = new qq.FineUploader({
    ...
    scaling: {
        sizes: [
            {name: "small", maxSize: 100},
            {name: "medium", maxSize: 300}
        ]
    }
});

The code above will result in Fine Uploader generating and uploading 2 scaled versions of each user-submitted image file. The original, along with the 2 scaled versions, will appear in the UI's file list as well. If the user submits a PNG named "image.png", that will be submitted, along with another file named "image (small).png", and "image (medium).png".

The scaled versions will be scaled proportional to the original image. In this case, the "small" version will be no more than 100px high or wide, and the "medium" version will be no more than 300px wide or high.

Notice that the two scaled images do not initially have file sizes displayed in the UI. This is due to the fact that Fine Uploader defers generation of these scaled versions until just before the images are going to be actually uploaded. After a scaled image has been uploaded, the client-side generated version of this image is discarded to conserve browser memory. Calls to the getFile API method will return the associated original file, not the scaled version. This is due to the fact that, again, the scaled version is not kept around any longer than necessary to conserve memory. Also, generating a scaled version is an asynchronous operation, which would require the getFile API method return a promise, introducing a breaking change to the API. Once a scaled image has been generated, the UI (and upload data store) will be updated with the generated size.

Fine Uploader ensures that a group of scaled images is always uploaded in order, smallest file first. The original file is always sent last.

Only Upload Scaled Files

To conserve bandwidth, you may only want to upload smaller, scaled versions of an original image. Simply set the scaling.sendOriginal option to false. The above example would be changed to:

var uploader = new qq.FineUploader({
    ...
    scaling: {
        sendOriginal: false,

        sizes: [
            {name: "small", maxSize: 100},
            {name: "medium", maxSize: 300}
        ]
    }
});

If you instruct Fine Uploader to NOT send the original file, it will not be uploaded, represented in the UI, or represented in the upload data store. Effectively, the original file will be transformed into the specified number of scaled versions. However, it will still be accessible via any API methods that require access to the original file for your convenience. For example, calling getFile on a scaled image will still return the original file.

Hide Scaled Files in UI Mode

If you do not want scaled versions to be represented in Fine Uploader UI's file list, simply set the Fine Uploader UI scaling.hideScaled option to true. Note that this will only ensure the scaled versions are not displayed in the UI. They will still be uploaded and represented in the upload data store. Since they will not be represented in the UI, this means that they cannot be deleted, canceled, or manually retried via Fine Uploader's default UI. Failures of these scaled versions will obviously not be apparent to users by default if you elect to hide them from the UI.

Include EXIF Headers in Scaled Images

If you set the scaling.includeExif option to true, Fine Uploader will insert the EXIF header from the original image into each scaled image. This option is ignored unless the original image AND the target (scaled) image are both of type "image/jpeg".

var uploader = new qq.FineUploader({
    ...
    scaling: {
        includeExif: true,

        sizes: [
            {name: "small", maxSize: 100},
            {name: "medium", maxSize: 300}
        ]
    }
});

In the above example, EXIF data from the original image will be inserted into both of the scaled images. Note that, by default, the scaled images will also be re-oriented according to the parsed Orientation tag in the original image. If you don't want Fine Uploader to do this, you must set the scaling.orient option to false.

Supported File Types

Scaling can occur on JPEGs, BMPs, GIFs, and PNGs. TIFFs are also supported, but only in Safari. The output type is generally limited to JPEG and PNG. If the original file type happens to be anything other than a JPEG, the output type will default to PNG. JPEG original files will be converted to scaled JPEGs by default. You can override this via the scaling option, but you should probably not, unless you know what you are doing.

Tracking Scaled Files Server-Side

Fine Uploader will send request parameters along with some scaled files to make it easy for you to connect them to the original/parent file server-side and group them together. "qqparentuuid" and "qqparentsize" parameters will be sent with each scaled image upload request. These parameters link the scaled image to the parent/original image. Note that the parent images will include a "qquuid" request parameter instead. If you are using Fine Uploader S3 or Azure, these parameters will be associated with the file in your bucket or blob container.

Using a third-party library to resize images

Fine Uploader's internal image resize code delegates to the drawImage method on the browser's native CanvasRenderingContext2D object. This object is used to manipulate a <canvas> element, which represents a submitted image File or Blob. Most browsers use linear interpolation when resizing images. This can lead to extreme aliasing and moire patterns which is a deal breaker for anyone resizing images for art/photo galleries, albums, etc. These kinds of artifacts are impossible to remove after the fact.

If speed is most important, and precise scaled image generation is not paramount, you should continue to use Fine Uploader's internal scaling implementation. However, if you want to generate higher quality scaled images for upload, you should instead use a third-party library to resize submitted image files, such as pica or limby-resize. As of version 5.10 of Fine Uploader, it is extremely easy to integrate such a plug-in into this library. In fact, Fine Uploader will continue to properly orient the submitted image file and then pass a properly sized <canvas> to the image scaling library of your choice to receive the resized image file, along with the original full-sized image file drawn onto a <canvas> for reference. The only caveat is that, due to issues with scaling larger images in iOS, you may need to continue to use Fine Uploader's internal scaling algorithm for that particular OS, as other third-party scaling libraries most likely do not contain logic to handle this complex case. Luckily, that is easy to account for as well.

If you'd like to, for example, use pica to generate higher-quality scaled images, simply pull pica into your project, and contribute a scaling.customResizer function, like so:

scaling: {
    customResizer: !qq.ios() && function(resizeInfo) {
        return new Promise(function(resolve, reject) {
            pica.resizeCanvas(resizeInfo.sourceCanvas, resizeInfo.targetCanvas, {}, resolve)
        })
    },
    ...
}

That's it! The above code will result in a higher-quality scaled image, and pica even pushes resizing logic off to a web worker to reduce strain on the UI thread.

Notices

  • Do not set the scaling.hideScaled option to true AND the scaling.sendOriginal option to false at the same time. This will result in no files being represented in the UI for images that are scalable.

  • Scaled files hidden from the UI when the scaling.hideScaled option is set to true cannot be canceled, manually retried, or deleted via the UI, since they are not represented in the UI.

  • Be very careful when specifying a value for scaling.defaultType, or a type property for one of your scaling.sizes objects. The only safe values are null (the default), "image/jpeg", and "image/png". Other target types may not be supported by all browsers.

  • Be careful if you set the validation.itemLimit option. Each Scaled image file will count towards this item limit.

  • Client-side scaling and EXIF header re-insertion operations can potentially be very resource-intensive. Please keep this in mind before you enable this feature, taking into account your user base and application goals.

  • iOS limits the size of a <canvas> to about 5 megapixels. Any image larger than this will be rendered as a blank <canvas>. This affects scaling as Fine Uploader uses <canvas> in part of the scaling process. To overcome this issue, Fine Uploader will further downsample scaled images that exceed this limit. You can check to see if such a limitation is known by examining the qq.supportedFeatures.unlimitedScaledImageSize flag.

  • The scaled file names will be sent with the upload request as the qqfilename parameter. Be sure to read this parameter when naming your file server-side.

Supported Browsers

This feature is supported on all browsers other than IE9 and older, Android 2.4 or older, and Safari 5.1 or older. Note that Android's stock browser is also not supported, due to multiple long-standing bugs in that browser with Blobs and data URIs. Chrome on Android is supported, though.