Skip to content

Instantly share code, notes, and snippets.

@dragermrb
Created May 17, 2020 12:43
Show Gist options
  • Save dragermrb/6d4b7fda5f183524d0ebe4b0a7d8635c to your computer and use it in GitHub Desktop.
Save dragermrb/6d4b7fda5f183524d0ebe4b0a7d8635c to your computer and use it in GitHub Desktop.
HTML5 Resize image before upload without ajax

HTML5 Resize image before upload without ajax

Allows to resize a file input image before upload to the server. The resized image will be attached to original file input. Then you can submit your form without ajax or handle it as you need.

Usage as jQuery Plugin

<form>
    <label>Select image</label>
    <input type="file" id="media_file" name="media_file" accept="image/*" capture="camera" class="form-control-file">

    <input type="submit" value="Upload"/>
</form>

<script src="js/jquery.min.js"></script>
<script src="js/jquery-image-upload-resizer.js"></script>
<script>
    $('#media_file').imageUploadResizer({
        max_width: 800, // Defaults 1000
        max_height: 600, // Defaults 1000
        quality: 0.8, // Defaults 1
        do_not_resize: ['gif', 'svg'], // Defaults []
    });
</script>

Usage as Vue Component

<form>
    <label>Select image</label>
    <image-upload-resizer-component
        name='media_file'
        :max-width="512"
        :max-height="512"
        :class-name="['form-control-file']"
        accept="image/*"
        capture="camera"
        doNotResize="['gif', 'svg']"
    ></image-upload-resizer-component>

    <input type="submit" value="Upload"/>
</form>
<template>
<input type="file" :id="id" :name="name" :class="className" @change="onChange" :accept="accept" :capture="capture" />
</template>
<script>
export default {
name: 'image-upload-resizer-component',
props: {
id: {
type: String,
default: 'media_file',
},
name: {
type: String,
default: 'media_file',
},
/**
* An integer in pixels for the maximum width allowed for uploaded images, selected images with a greater width than this value will be scaled down before upload.
* @default 1024
* @type {Number}
*/
maxWidth: {
type: Number,
default: 1024,
},
/**
* An integer in pixels for the maximum height allowed for uploaded images, selected images with a greater height than this value will be scaled down before upload.
* @default 1024
* @type {Number}
*/
maxHeight: {
type: Number,
default: 1024,
},
/**
* A float between 0 and 1.00 for the image quality to use in the resulting image data, around 0.9 is recommended.
* @default 1.00
* @type {Number}
*/
quality: {
type: Number,
default: 1.0,
},
/**
* Sets the desired class name for the input element
* @default {fileinput}
* @type {String or Array}
*/
className: {
type: [String, Array],
default: 'form-control-file',
},
/**
* Sets an optional capture attribute. (false, camera, user, environment)
* @default empty
* @type [String or Boolean]
*/
capture: {
type: [String, Boolean],
default: 'camera',
},
/**
* Sets the accept attribute, in case the same input can accept other files
* Shoub be a comma seperated string, eg 'audio/*,video/*,image/*'
* @default image/*
* @type {String}
*/
accept: {
type: String,
default: 'image/*',
},
/**
* An array of image's extensions that will not be resized (['gif', 'svg'])
* If only 1 extension, it can be provided directly as a stringEg ('gif')
* Disable all resizing with a catch all ('*')
* If not resized, the returned output will always be the unmodifed File object
* @default []
* @type {String or Array}
*/
doNotResize: {
type: [String, Array],
default: () => [],
},
},
data() {
return {
currentFile: {},
changeListenerEnabled: true,
};
},
methods: {
/**
* Get file from input
* @param {object} event
*/
onChange(e) {
if (!this.changeListenerEnabled){
console.log('ignore onchange');
return;
}
const file = e.target.files && e.target.files.length ? e.target.files[0] : null
if (file) {
this.handleFile(e.target, file)
}
},
/**
* Handels the file manipulation on upload
* @param {File} file The current original uploaded file
*/
handleFile(fileInput, originalFile) {
console.log('handleFile() is called with file:', originalFile)
if (!originalFile) {
return;
}
const mimetype = originalFile.type.split('/');
const isImage = originalFile.type.startsWith('image');
const doNotResize = typeof this.doNotResize === 'string'
? [this.doNotResize]
: this.doNotResize; // cast to array
// Don't resize if not image or doNotResize is set
if (!isImage || doNotResize.includes('*') || doNotResize.includes(mimetype[1])) {
console.log('No Resize');
return;
}
const that = this;
const img = document.createElement('img');
const canvas = document.createElement('canvas');
const reader = new FileReader();
reader.onload = function (e) {
img.src = e.target.result
img.onload = function () {
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
if (img.width < that.maxWidth && img.height < that.maxHeight) {
// Resize not required
return;
}
const ratio = Math.min(that.maxWidth / img.width, that.maxHeight / img.height);
const width = Math.round(img.width * ratio);
const height = Math.round(img.height * ratio);
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, width, height);
canvas.toBlob(function (blob) {
var resizedFile = new File([blob], 'resized_' + originalFile.name, originalFile);
var dataTransfer = new DataTransfer();
dataTransfer.items.add(resizedFile);
// temporary disable event listener, change and restore
that.changeListenerEnabled = false;
fileInput.files = dataTransfer.files;
that.changeListenerEnabled = true;
}, 'image/jpeg', that.quality);
}
}
reader.readAsDataURL(originalFile);
},
},
created() {
console.log('Initialised ImageUploader')
},
}
</script>
(function( $ ) {
$.fn.imageUploadResizer = function(options) {
var settings = $.extend({
max_width: 1000,
max_height: 1000,
quality: 1,
do_not_resize: [],
}, options );
this.filter('input[type="file"]').each(function () {
this.onchange = function() {
const that = this; // input node
const originalFile = this.files[0];
if (!originalFile || !originalFile.type.startsWith('image')) {
return;
}
// Don't resize if doNotResize is set
if (settings.do_not_resize.includes('*') || settings.do_not_resize.includes( originalFile.type.split('/')[1])) {
return;
}
var reader = new FileReader();
reader.onload = function (e) {
var img = document.createElement('img');
var canvas = document.createElement('canvas');
img.src = e.target.result
img.onload = function () {
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
if (img.width < settings.max_width && img.height < settings.max_height) {
// Resize not required
return;
}
const ratio = Math.min(settings.max_width / img.width, settings.max_height / img.height);
const width = Math.round(img.width * ratio);
const height = Math.round(img.height * ratio);
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, width, height);
canvas.toBlob(function (blob) {
var resizedFile = new File([blob], 'resized_'+originalFile.name, originalFile);
var dataTransfer = new DataTransfer();
dataTransfer.items.add(resizedFile);
// temporary remove event listener, change and restore
var currentOnChange = that.onchange;
that.onchange = null;
that.files = dataTransfer.files;
that.onchange = currentOnChange;
}, 'image/jpeg', settings.quality);
}
}
reader.readAsDataURL(originalFile);
}
});
return this;
};
}(jQuery));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment