Reputation: 765
I am using the fileupload jQuery plugin to upload files to my site. I would like to resize the images before upload to minimize the network usage but I could not find a full example so far. This is my simple test code.
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script src="/static/js/cloudinary/jquery.ui.widget.js" type="text/javascript"></script>
<script src="/static/js/cloudinary/load-image.min.js" type="text/javascript"></script>
<script src="/static/js/cloudinary/canvas-to-blob.min.js" type="text/javascript"></script>
<script src="/static/js/cloudinary/jquery.iframe-transport.js" type="text/javascript"></script>
<script src="/static/js/cloudinary/jquery.fileupload.js" type="text/javascript"></script>
<script src="/static/js/cloudinary/jquery.fileupload-ip.js" type="text/javascript"></script>
<script src="/static/js/cloudinary/jquery.fileupload-process.js" type="text/javascript"></script>
<script src="/static/js/cloudinary/jquery.fileupload-validate.js" type="text/javascript"></script>
...
$("#fileupload")
.fileupload({
disableImageResize: false,
imageMaxWidth: 8000,
imageMaxHeight: 6000,
acceptFileTypes: /(\.|\/)(gif|jpe?g|png|bmp|ico)$/i,
maxFileSize: 20000000, // 20MB
process: [
{
action: 'load',
fileTypes: /^image\/(gif|jpeg|png)$/,
maxFileSize: 20000000 // 20MB
},
{
action: 'resize',
maxWidth: 200,
maxHeight: 150,
minWidth: 80,
minHeight: 60
},
{
action: 'save'
}
]
});
Upvotes: 0
Views: 4035
Reputation: 97
jQuery Drag and Drop File Upload | Bootstrap Image Upload and Preview
// Code By Webdevtrick ( https://webdevtrick.com )
function readFile(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function(e) {
var htmlPreview =
'<img width="200" src="' + e.target.result + '" />' +
'<p>' + input.files[0].name + '</p>';
var wrapperZone = $(input).parent();
var previewZone = $(input).parent().parent().find('.preview-zone');
var boxZone = $(input).parent().parent().find('.preview-zone').find('.box').find('.box-body');
wrapperZone.removeClass('dragover');
previewZone.removeClass('hidden');
boxZone.empty();
boxZone.append(htmlPreview);
};
reader.readAsDataURL(input.files[0]);
}
}
function reset(e) {
e.wrap('<form>').closest('form').get(0).reset();
e.unwrap();
}
$(".dropzone").change(function() {
readFile(this);
});
$('.dropzone-wrapper').on('dragover', function(e) {
e.preventDefault();
e.stopPropagation();
$(this).addClass('dragover');
});
$('.dropzone-wrapper').on('dragleave', function(e) {
e.preventDefault();
e.stopPropagation();
$(this).removeClass('dragover');
});
$('.remove-preview').on('click', function() {
var boxZone = $(this).parents('.preview-zone').find('.box-body');
var previewZone = $(this).parents('.preview-zone');
var dropzone = $(this).parents('.form-group').find('.dropzone');
boxZone.empty();
previewZone.addClass('hidden');
reset(dropzone);
});
/* Code By Webdevtrick ( https://webdevtrick.com ) */
.container {
padding: 50px 10%;
}
.box {
position: relative;
background: #ffffff;
width: 100%;
}
.box-header {
color: #444;
display: block;
padding: 10px;
position: relative;
border-bottom: 1px solid #f4f4f4;
margin-bottom: 10px;
}
.box-tools {
position: absolute;
right: 10px;
top: 5px;
}
.dropzone-wrapper {
border: 2px dashed #91b0b3;
color: #92b0b3;
position: relative;
height: 150px;
}
.dropzone-desc {
position: absolute;
margin: 0 auto;
left: 0;
right: 0;
text-align: center;
width: 40%;
top: 50px;
font-size: 16px;
}
.dropzone,
.dropzone:focus {
position: absolute;
outline: none !important;
width: 100%;
height: 150px;
cursor: pointer;
opacity: 0;
}
.dropzone-wrapper:hover,
.dropzone-wrapper.dragover {
background: #ecf0f5;
}
.preview-zone {
text-align: center;
}
.preview-zone .box {
box-shadow: none;
border-radius: 0;
margin-bottom: 0;
}
.btn-primary {
background-color: crimson;
border: 1px solid #212121;
}
<!DOCTYPE html>
<!-- Code By Webdevtrick ( https://webdevtrick.com ) -->
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>Drag and Drop Image Upload | Webdevtrick.com</title>
<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css'>
<link rel="stylesheet" href="style.css">
</head>
<body>
<section>
<form action="" method="POST" enctype="multipart/form-data">
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label class="control-label">Upload File</label>
<div class="preview-zone hidden">
<div class="box box-solid">
<div class="box-header with-border">
<div><b>Preview</b></div>
<div class="box-tools pull-right">
<button type="button" class="btn btn-danger btn-xs remove-preview">
<i class="fa fa-times"></i> Reset The Field
</button>
</div>
</div>
<div class="box-body"></div>
</div>
</div>
<div class="dropzone-wrapper">
<div class="dropzone-desc">
<i class="glyphicon glyphicon-download-alt"></i>
<p>Choose an image file or drag it here.</p>
</div>
<input type="file" name="img_logo" class="dropzone">
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<button type="submit" class="btn btn-primary pull-right">Upload</button>
</div>
</div>
</div>
</form>
</section>
<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js'></script>
<script src="function.js"></script>
</body>
</html>
Upvotes: 1
Reputation: 83438
As putting all the pieces together working for a complex uploader is a complex task, I share here my solution.
The elements of this answer
There can be arbitrary number of uploaders per page. In this example there are two separate upload buttons on the page, called driving and medical.
Resize the image to 1024 x 1024 on the client-side before uploading
Show preview of uploaded image for the user during the upload
HTML code as Jinja 2 templates
Bootstrap 3.x themed upload button and progress bar for the upload
Pyramid style JavaScript resource loading
Jinja 2 HTML template code which renders one individual upload widget (upload_snippet.html
), it takes parameters id and name and upload_target:
<div id="upload-{{ id }}">
<div id="medical-license" class="btn btn-block btn-file">
<i class="fa fa-camera"></i> {{ name }}
<input type="file" class="file-select" data-url="{{ upload_target }}" data-param-name="{{ id }}">
</div>
<p>
<div class="preview" style="display: none"></div>
</p>
<div class="progress progress-xxx" style="display: none">
<div class="progress-bar progress-bar-striped progress-bar-xxx active" role="progressbar" aria-valuenow="45" aria-valuemin="0" aria-valuemax="100" style="width: 0%">
</div>
</div>
<div class="success" style="display: none">
<div class="alert alert-success">
{{ name }} upload completed
</div>
</div>
<div class="error" style="display: none">
<div class="alert alert-danger">
<span class="error-message"></span>
</div>
</div>
</div>
The main Jinja 2HTML template which constructs two upload widgets. It styles them to look like Bootstrap buttons:
{% extends "site/base.html" %}
{% block extra_head %}
<style>
/* http://www.abeautifulsite.net/whipping-file-inputs-into-shape-with-bootstrap-3/ */
.btn-file {
position: relative;
overflow: hidden;
}
.btn-file input[type=file] {
position: absolute;
top: 0;
right: 0;
min-width: 100%;
min-height: 100%;
font-size: 100px;
text-align: right;
filter: alpha(opacity=0);
opacity: 0;
outline: none;
background: white;
cursor: inherit;
display: block;
}
.preview {
width: 128px;
height: 128px;
margin: 0 auto;
}
</style>
{% endblock %}
{% block content_section %}
<!-- Header -->
<section id="license-information">
<div class="row">
<div class="col-md-12">
<h1>Upload information</h1>
</div>
</div>
<div class="row">
<div class="col-md-6">
{% with id='medical', name='Medical license' %}
{% include "upload_snippet.html" %}
{% endwith %}
{% with id='driving', name='Driving license or other id' %}
{% include "upload_snippet.html" %}
{% endwith %}
</div>
</div>
</section>
{% endblock content_section %}
{% block custom_script %}
<!-- The jQuery UI widget factory, can be omitted if jQuery UI is already included -->
<script src="{{ 'xxx:static/jquery-file-upload/js/vendor/jquery.ui.widget.js'| static_url }}"></script>
<!-- The Load Image plugin is included for the preview images and image resizing functionality -->
<script src="{{ 'xxx:static/jquery-file-upload/js/load-image.all.min.js' | static_url }}"></script>
<!-- The Canvas to Blob plugin is included for image resizing functionality -->
<script src="{{ 'xxx:static/jquery-file-upload/js/canvas-to-blob.js' | static_url }}"></script>
<!-- The basic File Upload plugin -->
<script src="{{ 'xxx:static/jquery-file-upload/js/jquery.fileupload.js' | static_url }}"></script>
<!-- The File Upload processing plugin -->
<script src="{{ 'xxx:static/jquery-file-upload/js/jquery.fileupload-process.js' | static_url }} "></script>
<!-- The File Upload image preview & resize plugin -->
<script src="{{ 'xxx:static/jquery-file-upload/js/jquery.fileupload-image.js' | static_url }} "></script>
<script>
window.nextURL = "{{ after_both_files_are_uploaded }}"
</script>
<script>
"use strict";
var state = {
medical: false,
driving: false
}
// Make styled elements to trigger file input
$(document).on('change', '.btn-file :file', function() {
var input = $(this),
numFiles = input.get(0).files ? input.get(0).files.length : 1,
label = input.val().replace(/\\/g, '/').replace(/.*\//, '');
input.trigger('fileselect', [numFiles, label]);
});
function checkForward() {
// Is all upload done and we can go to the next page?
if(state.medical && state.driving) {
window.location = window.nextURL;
}
}
function doUpload(name) {
var baseElem = $("#upload-" + name);
if(baseElem.length != 1) {
throw new Error("Wooops, bad DOM tree");
}
function onStart() {
baseElem.find(".progress").show();
baseElem.find(".error").hide();
baseElem.find(".success").hide();
}
function onDone(result, data) {
baseElem.find(".progress").hide();
if(data.result.status == "ok") {
// All ok, check if we can proceed
baseElem.find(".success").show();
state[name] = true;
checkForward();
} else {
// Server responded us it didn't like the file and gave a specific error message
var msg = data.result.message;
baseElem.find(".error-message").text(msg);
baseElem.find(".error").show();
state[name] = false;
}
}
function onError(result, data) {
baseElem.find(".progress").hide();
baseElem.find(".error-message").text("Upload could not be completed. Please contact the support.");
baseElem.find(".error").show();
state[name] = false;
}
function onProgress(e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10);
baseElem.find(".progress-bar").css("width", progress + "%");
}
function onPreview(e, data) {
var canvas = data.files[0].preview;
var dataURL = canvas.toDataURL();
baseElem.find(".preview").css("background-image", 'url(' + dataURL +')');
baseElem.find(".preview").css({width: canvas.width, height: canvas.height});
baseElem.find(".preview").show();
}
var upload = baseElem.find('.file-select');
upload.fileupload({
dataType: 'json',
// Enable image resizing, except for Android and Opera,
// which actually support image resizing, but fail to
// send Blob objects via XHR requests:
// disableImageResize: /Android(?!.*Chrome)|Opera/
// .test(window.navigator && navigator.userAgent),
disableImageResize: false,
imageMaxWidth: 1024,
imageMaxHeight: 1024,
imageCrop: false, // Force cropped images,
previewMaxWidth: 128,
previewMaxHeight: 128,
maxFileSize: 7*1024*1024
});
upload.bind("fileuploaddone", onDone);
upload.bind("fileuploadstart", onStart);
upload.bind("fileuploadfail", onError);
upload.bind("fileuploadprogress", onProgress);
upload.bind('fileuploadprocessalways', onPreview);
}
$(document).ready(function() {
doUpload("medical");
doUpload("driving");
});
</script>
{% endblock %}
Then a simple server-side Pyramid view which decodes the payload and checks the user uploaded an image and not a random file. The result is an JSON response which JavaScript can decode:
@view_config(route_name='upload_target', renderer='json')
def upload_target(request):
"""AJAX upload of driving license and medical images."""
if "medical" in request.params:
license = "medical"
files = request.params["medical"]
elif "driving" in request.params:
license = "driving"
files = request.params["driving"]
else:
raise RuntimeError("Unsupported upload type")
# # TODO: use chunks, do not buffer 100%
# path = user.prepare_upload_path()
storage = io.open(where_to_save, "wb")
fp = files.file
# Now we test for a valid image upload
image_test = imghdr.what(fp)
if image_test == None:
return {"status": "fail", "message": "Only JPEG and PNG image file upload supported."}
fp.seek(0)
data = fp.read()
assert len(data) > 0
storage.write(data)
storage.close()
return {"status": "ok"}
Upvotes: 0
Reputation: 747
I had a similar problem which I believe was due to the absence of canvas-to-blob.js. Adding that script, got it working. I was using chrome. See this issue on github:
https://github.com/blueimp/jQuery-File-Upload/issues/2665
Also, you probably don't need describe the whole processing pipeline description either, as it jquery file upload builds the necessary stages based on the presence of certain parameters.
Upvotes: 0