locked
resize image before upload via background transfer in html/js windows universal app 8.1 RRS feed

  • Question

  • I would like to resize an image picked from the gallery of the phone before uploading it via background transfer so far I have:-

    filePicker.pickSingleFileAsync().then(function (file) {
                                uploadSingleFileAsync(uri, file);
                            }).done(null, displayException);
    
    function uploadSingleFileAsync(uri, file) {
                        if (!file) {
                            displayError("Error: No file selected.");
                            return;
                        }
                        return file.getBasicPropertiesAsync().then(function (properties) {
                            if (properties.size > maxUploadFileSize) {
                                displayError("Selected file exceeds max. upload file size (" + (maxUploadFileSize / (1024 * 1024)) +
                                    " MB).");
                                return;
                            }
                            var upload = new UploadOperation();
                            //tried this to compress the file but it doesnt work obviously not right for the object
                            //file = file.slice(0, Math.round(file.size / 2));
                            upload.start(uri, file);
                            // Persist the upload operation in the global array.
                            uploadOperations.push(upload);
                        });
                    }

    and the rest then uploads the file. I tried adding in .slice but it doesn't work (im guessing because file is an object rather than) and I'm not sure how to compress this type of file object. I can't seem to find any examples or advice on msdn or the windows dev forums, I can obviously resize the photos once they are on the server but I would rather users are not waiting longer than they have to for their files to upload. I would quite like to be able to resize audio and video files as well if that is at all possible...

    Do I need to save the image before I can manipulate it? Any advice would be greatly appreciated!


    Saturday, August 16, 2014 2:17 PM

All replies

  • You can use a BitmapEncoder to reencode the bitmap at a smaller size. See How to edit an image (HTML) for the basics.

    Saturday, August 16, 2014 2:48 PM
  • Thanks for your fast reply, I have tried this method but i keep getting an error on the line:-

    file.openAsync(Windows.Storage.FileAccessMode.readWrite)

    SCRIPT6009: Exception is about to be caught by JavaScript library code at line 2548, column 13 - JavaScript runtime error: The specified file is read only.
    File: demo.js, Line: 2548, Column: 13

    How do I get write access to the file?

    the function that is called on continuation of the phone app is

    function loadSaveFileAsync(file) {
            // Keep data in-scope across multiple asynchronous methods.
            var inputStream;
            var outputStream;
            var encoderId;
            var pixels;
            var pixelFormat;
            var alphaMode;
            var dpiX;
            var dpiY;
            var outputFilename;
            var ScaleFactor = 0.5;

            new WinJS.Promise(function (comp, err, prog) { comp(); }).then(function () {
                // On Windows Phone, this call must be done within a WinJS Promise to correctly
                // handle exceptions, for example if the file is read-only.
                return FutureAccess.getFileAsync(FileToken);
            }).then(function (inputFile) {
                return inputFile.openAsync(Windows.Storage.FileAccessMode.read);
            }).then(function (stream) {
                inputStream = stream;
                return Windows.Graphics.Imaging.BitmapDecoder.createAsync(inputStream);
            }).then(function (decoder) {
                var transform = new Windows.Graphics.Imaging.BitmapTransform();

                // Scaling occurs before flip/rotation, therefore use the original dimensions
                // (no orientation applied) as parameters for scaling.
                // Dimensions are rounded down by BitmapEncoder to the nearest integer.
                transform.scaledHeight = decoder.pixelHeight * ScaleFactor;
                transform.scaledWidth = decoder.pixelWidth * ScaleFactor;
                transform.rotation = Helpers.convertToBitmapRotation(UserRotation);

                // Fant is a relatively high quality interpolation mode.
                transform.interpolationMode = Windows.Graphics.Imaging.BitmapInterpolationMode.fant;

                // The BitmapDecoder indicates what pixel format and alpha mode best match the
                // natively stored image data. This can provide a performance and/or quality gain.
                pixelFormat = decoder.bitmapPixelFormat;
                alphaMode = decoder.bitmapAlphaMode;
                dpiX = decoder.dpiX;
                dpiY = decoder.dpiY;

                // Get pixel data from the decoder. We apply the user-requested transforms on the
                // decoded pixels to take advantage of potential optimizations in the decoder.
                return decoder.getPixelDataAsync(
                    pixelFormat,
                    alphaMode,
                    transform,
                    Windows.Graphics.Imaging.ExifOrientationMode.respectExifOrientation,
                    Windows.Graphics.Imaging.ColorManagementMode.colorManageToSRgb
                    );
            }).then(function (pixelProvider) {
                pixels = pixelProvider.detachPixelData();

                // The destination file was passed as an argument to loadSaveFileAsync().
                outputFilename = file.name;
                switch (file.fileType) {
                    case ".jpg":
                        encoderId = Windows.Graphics.Imaging.BitmapEncoder.jpegEncoderId;
                        break;
                    case ".bmp":
                        encoderId = Windows.Graphics.Imaging.BitmapEncoder.bmpEncoderId;
                        break;
                    case ".png":
                    default:
                        encoderId = Windows.Graphics.Imaging.BitmapEncoder.pngEncoderId;
                        break;
                }

                return file.openAsync(Windows.Storage.FileAccessMode.readWrite);
            }).then(function (stream) {
                outputStream = stream;

                // BitmapEncoder expects an empty output stream; the user may have selected a
                // pre-existing file.
                outputStream.size = 0;
                return Windows.Graphics.Imaging.BitmapEncoder.createAsync(encoderId, outputStream);
            }).then(function (encoder) {
                // Write the pixel data onto the encoder. Note that we can't simply use the
                // BitmapTransform.ScaledWidth and ScaledHeight members as the user may have
                // requested a rotation (which is applied after scaling).
                encoder.setPixelData(
                    pixelFormat,
                    alphaMode,
                    DisplayWidthNonScaled * ScaleFactor,
                    DisplayHeightNonScaled * ScaleFactor,
                    dpiX,
                    dpiY,
                    pixels
                    );

                return encoder.flushAsync();
            }).then(function () {
                WinJS.log && WinJS.log("Successfully saved a copy: " + outputFilename, "sample", "status");
            }, function (error) {
                WinJS.log && WinJS.log("Failed to update file: " + error.message, "sample", "error");
                resetSessionState();
                resetPersistedState();
            }).then(function () {
                // Finally, close each stream to release any locks.
                inputStream && inputStream.close();
                outputStream && outputStream.close();
            }).then(function () {
                var upload = new UploadOperation();
                upload.start(uri, file);
                // Persist the upload operation in the global array.
                uploadOperations.push(upload);

            });
        }


    Thursday, September 4, 2014 1:57 PM
  • You should not overwrite the user's image with a compressed version. I'm pretty sure they'd like to keep the original version locally. The easiest would be to just create a new temporary file in your App's local storage and save the resized version there. Upon completion of the upload you can safely delete it.
    Thursday, September 4, 2014 2:41 PM
  • At what stage and how would I go about doing that? I just wanted to compress the images before upload but it is getting a little complicated...
    Thursday, September 4, 2014 4:44 PM
  • Your changes would occur roughly around this line: return file.openAsync(Windows.Storage.FileAccessMode.readWrite);

    Instead of using the file you received you create a new on in your App's local folder. You can find information on how to do this in this article: http://blog.jerrynixon.com/2012/06/windows-8-how-to-read-files-in-winrt.html

    • Proposed as answer by Oliver Ulm Thursday, September 4, 2014 9:00 PM
    Thursday, September 4, 2014 5:29 PM
  • PERFECT that was just the answer I was searching for!! Thank you so much!!
    Thursday, September 4, 2014 8:23 PM