Compressing Images and other files before uploading to the server


#1

I’m working on an application with image and file sharing functionality.
I use nativescript-background-http to upload images to the server, but it’s very slow for large images.
Is there a good way to compress or scale down(re-sample) images before uploading them ?

I see examples using toBase64String, but It I’m not sure how to save to base64 image to the server using nativescript-background-http. I tried several times but it didn’t work. Can someone share a complete example of converting image to base64 then posting to the server with nativescript-background-http?

Also, is there a way to compress other files such as documents, or videos?


#2

Here’s how I handled compressing images for use with nativescript-background-http

in the code behind

function doUpload(source, imageToPush, fromWhere) {
    stackLayout.visibility = "visible";
    const folder = fs.knownFolders.documents().path;
    const fileName = "yourimagename.png";
    const path = fs.path.join(folder, fileName);
    let saved;
    if (fromWhere === "fromGallery") {
        // This method is working well for images from gallery
        const b64 = source.toBase64String("jpg", 10);
        const fileSize = b64.replace(/\=/g, "").length * 0.75;
        if (fileSize.length > 5) {
            // If file is greater than 1 MB
            source.loadFromBase64(b64);
        }
        saved = source.saveToFile(path, "jpg", 100);
    } else if (fromWhere === "fromCamera") {
        // This method is working well for images from camera
        saved = source.saveToFile(path, "png", 100);
    }

    if (saved) {
        // get the background task object
        const task = studio.uploadImage(path);

        const logEvent = function(e) {
            console.log(e.eventName);
            if (e.eventName === "complete") {
                const response = e.response.getBodyAsString();

                // do something with response
                
                progress.set("uploadTask", { upload: 0, totalUpload: 0 });
                stackLayout.visibility = "collapse";
            }
        };

        task.on("progress", logEvent);
        task.on("error", logEvent);
        task.on("complete", logEvent);
        progress.set("uploadTask", task);
    }
}

and then in the view model

viewModel.uploadImage = function(imageUrl) {
        const token = applicationSettings.getString("accessToken");
        const form = new FormData(); // eslint-disable-line no-undef
        form.append("file", imageUrl);

        const bghttp = require("nativescript-background-http");

        const session = bghttp.session("image-upload");

        const request = {
            url: "YOUR_BACKEND_API_URL",
            method: "POST",
            headers: {
                "Content-Type": "application/octet-stream",
                "File-Name": "yourimagename",
                Authorization: token
            },
            description: "your description"
        };

        const params = [
            { name: "studioimg", value: "value" },
            { name: "fileToUpload", filename: imageUrl, mimeType: "image/jpeg" }
        ];
        const task = session.multipartUpload(params, request);

        return task;
    };

Hope this helps you :slight_smile:


Upload image to server
#3

Thanks for your response. I have tried your method. But I had to replace “jpg” in const b64 = source.toBase64String(“jpg”, 10), with jpeg for it to work. Base64String with jpg was giving me null

I also noticed that changing the compression ratio from 10 to any other lower number like 5 or even 2, gives the same file size. So I’m not sure if it’s actually compressing. Have you noticed that too?


#4

No, I had not noticed that before :thinking:


#5

Hi, I have a solution where I resize and compress one image:

/**
 * cambia el tamaño de la ruta de la imagen que se envia y se guarda la nueva imagen
 * @param {type} localPathSm    absolute path de la nueva imagen
 * @param {type} localPath     .android: absolute path de la imagen original
 * @param {type} orientacion    integer, para saber la orientacion de la foto
 * @param {type} targetW        nuevo tamaño, width
 * @param {type} targetH        nuevo tamaño, height
 * @param {type} flgRotar       true se rota la imagen, false no rota
 * @returns {Promise|guardarImagenResize.promise}
 */
function guardarImagenResize(localPathSm,localPath,orientacion,targetW,targetH,flgRotar) {
    var promise;
    promise = new Promise(function (resolve, reject) {
        if (application.android) {
            try{
                //se obtiene el width y el height de la imangen sin cargarla en memoria con inJustDecodeBounds = true
                var bmOptions = new android.graphics.BitmapFactory.Options();
                bmOptions.inJustDecodeBounds = true;
                android.graphics.BitmapFactory.decodeFile(localPath, bmOptions);
                var photoW = bmOptions.outWidth;
                var photoH = bmOptions.outHeight;
                console.log(photoW+", "+photoH);
                
                var destW=targetW;
                var destHeight = photoH/( photoW / targetW );
                //si la imagen original esta en portraid intercambiar tamaños
                if(destHeight>2100){
                    destW=targetH;
                    destHeight = photoH/( photoW / targetH );
                }
                // 6 = ORIENTATION_ROTATE_90, 8 = ORIENTATION_ROTATE_270
                if(flgRotar && orientacion===6 || orientacion==='6' || orientacion===8 || orientacion==='8'){
                    destW=destHeight;
                    destHeight=targetW;
                }
                
                bmOptions.inJustDecodeBounds = false;
                bmOptions.inScaled = false;
                bmOptions.inDither = false;
                bmOptions.inPreferredConfig = android.graphics.Bitmap.Config.ARGB_8888;
                var bitmapSmall = android.graphics.BitmapFactory.decodeFile(localPath,bmOptions);
                var bitmapNewSize = null;
                if(flgRotar){
                    bitmapNewSize = rotateBitmap(bitmapSmall, orientacion);
                }else{
                    bitmapNewSize = bitmapSmall;
                }
                console.log("destHeight:"+destHeight);
                var b2 = android.graphics.Bitmap.createScaledBitmap(bitmapNewSize, destW, destHeight, true);
                var outStream = new java.io.ByteArrayOutputStream();
                b2.compress(android.graphics.Bitmap.CompressFormat.JPEG, 90, outStream);
                var nuevoArchivo = new java.io.File(localPathSm);
                nuevoArchivo.createNewFile();
                var archivoOut = new java.io.FileOutputStream(nuevoArchivo);
                archivoOut.write(outStream.toByteArray());
                archivoOut.close();

                archivoOut=null;
                nuevoArchivo=null;
                outStream=null;
                
                b2.recycle();
                bitmapSmall.recycle();
                if(flgRotar){
                    bitmapNewSize.recycle();
                }
                resolve("ok");
            }catch(ex){
                console.log(ex);
                resolve("error:"+ex);
            }
        }else{
            resolve("no hay dato");
        }
    });
    return promise;
};
exports.guardarImagenResize = guardarImagenResize;


function rotateBitmap(bitmap,orientation){
    var matrix = new android.graphics.Matrix();
    switch (orientation) {
        case android.media.ExifInterface.ORIENTATION_NORMAL:
            return bitmap;
        case android.media.ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
            matrix.setScale(-1, 1);
            break;
        case android.media.ExifInterface.ORIENTATION_ROTATE_180:
            matrix.setRotate(180);
            break;
        case android.media.ExifInterface.ORIENTATION_FLIP_VERTICAL:
            matrix.setRotate(180);
            matrix.postScale(-1, 1);
            break;
        case android.media.ExifInterface.ORIENTATION_TRANSPOSE:
            matrix.setRotate(90);
            matrix.postScale(-1, 1);
            break;
       case android.media.ExifInterface.ORIENTATION_ROTATE_90:
           matrix.setRotate(90);
           break;
       case android.media.ExifInterface.ORIENTATION_TRANSVERSE:
           matrix.setRotate(-90);
           matrix.postScale(-1, 1);
           break;
       case android.media.ExifInterface.ORIENTATION_ROTATE_270:
           matrix.setRotate(-90);
           break;
       default:
           return bitmap;
    }
    try {
        var bmRotated = android.graphics.Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
        bitmap.recycle();
        return bmRotated;
    }
    catch (e) {
        console.log(e);
        return null;
    }
};
exports.rotateBitmap = rotateBitmap;

#6

It looks interesting. Especially because it can also resize the image. I will try that too. @christian.ventura