I am working on web page where I want to use drag an drop image upload system with options for remove and sort images before upload.
I would like to ask you about your opinion if this system logic is good.
I do not know if this system will have some troubles in future bcz all systems which I found have different logic.
My logic:
My system is based mainly on JS and images will be UPLOAD to folder AFTER FORM SUBMIT when whole form (other data about user as name, phone, etc) is filled in. Every modification as remove or change order is done by modify object data via JS.
Web sources:
They are based on UPLOAD images immediately AFTER DROP/SELECT IMAGE and upload to server folder via PHP.
DIFFERENCE: My system no need to send data to PHP every time if one of this actions is happened - new image is added, or deleted or order is changed.
I will be very thankful even if you help me find some other problems. Also I hope it can help someone who is building own system.
Here is my code:
JS:
/* DRAG AND DROP IMG UPLOAD */
// preventing page from redirecting
$("html").on("dragover", function(e) {
e.preventDefault();
e.stopPropagation();
});
$("html").on("drop", function(e) { e.preventDefault(); e.stopPropagation(); });
// Drag enter
$('.add-item__upload-area').on('dragenter', function (e) {
e.stopPropagation();
e.preventDefault();
});
// Drag over
$('.add-item__upload-area').on('dragover', function (e) {
e.stopPropagation();
e.preventDefault();
$('.add-item__dropzone').css({border: '2px dashed #111'});
});
$('.add-item__upload-area').on('dragleave', function (e) {
e.stopPropagation();
e.preventDefault();
$('.add-item__dropzone').css({border: '2px dashed #888'});
});
// Open file browser
$('.add-item__add-photo-btn').click(function() {
$('#input-files').trigger('click'); // execute input type file after click on button
event.preventDefault();
});
// create object to keep img files for modification
var data = {};
// Files added by drop to dropzone area
$('.add-item__upload-area').on('drop', function (e) {
e.stopPropagation();
e.preventDefault();
var totalFiles = e.originalEvent.dataTransfer.files.length;
// fill form by files from input
for (var i = 0; i < totalFiles; i++) {
var file = e.originalEvent.dataTransfer.files[i]; // Get list of the files dropped to area
// fill object for file modifications by img files
data['file_'+i] = file;
// create temp url to img object for creating thumbnail and append img with temp src to thumbnail__wraper div
$('.thumbnail__wrapper').append('<div class="thumbnail" id="file_'+ i +'"><img class="imageThumb" src="' + URL.createObjectURL(file) + '" title="' + file.name + '"/><br/><a class="remove">ZmazaƄ</a></div>');
$('.add-item__add-photo-btn-area').find('p').hide();
}
});
// Files added by selecting in file browser
$('#input-files').on('change', function(){
var totalFiles = $('#input-files')[0].files.length; // get number of uploaded files from input element
// modify ordering of fields inside data object according to order from sortable ui
for (var i = 0; i < totalFiles; i++) {
// By appending [0] to the jQuery object will return the first DOM element. Get <input> element from object.
var file = $('#input-files')[0].files[i]; // Get first file from list of files selected with the <input type="file">
// fill object for file modifications by img files
data['file_'+i] = file;
// create temp url to img object for creating thumbnail and append img with temp src to thumbnail__wraper div
$('.thumbnail__wrapper').append('<div class="thumbnail" id="file_'+ i +'"><img class="imageThumb" src="' + URL.createObjectURL(file) + '" title="' + file.name + '"/><br/><a class="remove">ZmazaƄ</a></div>');
$('.add-item__add-photo-btn-area').find('p').hide();
}
//console.log(data);
//uploadData(formData); // send data via ajax to upload.php
});
// Remove field of data obj after click on remove button
$('.add-item__dropzone').on('click','.remove', function() {
// remove choosen field
delete data[$(this).parent().attr('id')];
$(this).parent('.thumbnail').remove();
//console.log($(this).parent().attr('id'));
//console.log(data);
});
// Make images sortable
$('.thumbnail__wrapper').sortable({
axis: 'x', // axis in which sort is possible
update: function (event, ui) {
// get all ids (item-1, ....) from li elements (setted as sortable) of list in form item[]=1&item[]=2
var reorderList = $(this).sortable('serialize');
//console.log(reorderList);
// fill FormData object by files from data array after order change
var formData = new FormData();
var dataLength = Object.keys(data).length;
//console.log(data);
data = changeOrder(data, reorderList);
//console.log(data);
}
})
// Change order of files inside object data
function changeOrder(obj, order) {
var newObject = {};
// if the g flag is used, all results matching the complete regular expression will be returned, but capturing groups will not.
var matches = order.match(/=/g);
var count = matches.length;
// file[]=1&file[]=0&file[]=2 => ["file[]=1", "file[]=0", "file[]=2"]
var arrayOfOrderValues = order.split('&');
// console.log(arrayOfOrderValues);
for (var i = 0; i < count; i++) {
// get values in appropriate format ["file[]=1", "file[]=0", "file[]=2"] => file_1, file_0, file_2
orderKey = 'file_' + arrayOfOrderValues[i].substring(arrayOfOrderValues[i].lastIndexOf("=") + 1);
// console.log(orderKeyValue);
// check if object contains key(property) as orderKeyValue and then do next step
if(obj.hasOwnProperty(orderKey)) {
// fill value of obj['file_1'] to newObject['file_0'] - ex. in first loop
newObject['file_'+i] = obj[orderKey];
}
}
return newObject;
}
// Sending AJAX request and upload file
function uploadData(formdata){
$.ajax({
url: '_inc/upload.php',
type: 'post',
data: formdata,
contentType: false,
processData: false,
dataType: 'json',
success: function(response){
//addThumbnail(response); // if success - create thumbnail
}
})
}
$('.test').on('submit', function() {
event.preventDefault();
var formData = new FormData(); // Create form
console.log(data);
var count = Object.keys(data).length; // count fields of object
for (var i = 0; i < count; i++) {
formData.append('files[]', data['file_'+i]); // append data to form -> (key, value), file[0] first file from list of files !!! key must have [] for array
}
console.log(count);
uploadData(formData); // send data via ajax to upload.php
})
PHP:
<?php
require($_SERVER['DOCUMENT_ROOT'] . '/bazar/_inc/DBController.php');
// inicialize object
$db_handler = new DBController();
// get total number of files in file list
$total_files = count($_FILES['files']['name']);
// array(5) { ["name"]=> array(2) { [0]=> string(23) "IMG_20190916_105311.jpg" [1]=> string(19) "20180525_115635.jpg" } ["type"]=> array(2) { [0]=> string(10) "image/jpeg" [1]=> string(10) "image/jpeg" } ["tmp_name"]=> array(2) { [0]=> string(28) "/home/gm016900/tmp/phpdU58AU" [1]=> string(28) "/home/gm016900/tmp/phpIqWoaQ" } ["error"]=> array(2) { [0]=> int(0) [1]=> int(0) } ["size"]=> array(2) { [0]=> int(306091) [1]=> int(2315700) } }
// Create array for jQuery information
$return_arr = array();
$images_names = array_filter($_FILES['files']['name']); // filter just values for $key = name
// -> array(2) { [0]=> string(23) "IMG_20190916_105311.jpg" [1]=> string(19) "20180525_115635.jpg" }
//var_dump($images_names);
/* BATCH FILE UPLOAD */
// if $_FILES contains image then do foreach
if ( !empty($images_names) ) {
for($i = 0; $i < $total_files; $i++) {
// Get reference to uploaded image
$image_file = $_FILES["files"];
// Get image name
$image_name = $_FILES["files"]["name"][$i];
// Get file size
$image_size = $_FILES["files"]["size"][$i];
/* BASIC CONTROL */
// Exit if no file uploaded or image name contains unvalid characters /, \\
if ( ( !strpos($image_name, '/') || !strpos($image_name, '\\') ) && isset($image_file) ) {
// define variables if image in correct format is uploaded
$errors = array();
$maxsize = 10485760;
$acceptable = array(
'image/jpeg',
'image/jpg',
'image/gif',
'image/png'
);
} else {
die('No image uploaded.');
}
// Exit if image file is zero bytes or if image size is more then 10 MB
if (getimagesize($image_file["tmp_name"][$i]) <= 0) {
die('Uploaded file has no contents.');
} elseif ($image_size >= $maxsize) {
die('Image is too large. Image must be less than 10 megabytes.');
}
// Determine the type of an image int|false
$image_type = exif_imagetype($image_file["tmp_name"][$i]);
// Exit if is not a valid image file or image has not supported type
if (!$image_type) {
die('Uploaded file is not an image.');
} elseif ( !in_array($image_file["type"][$i], $acceptable) ) {
die('Image has not supported type JPG, PNG, GIF.');
} else {
$src = "default.png";
}
/* CREATE RANDOM IMAGE NAME INSTEDAOF $_FILES['files']['name'] */
// Get file extension based on file type, to prepend a dot we pass true as the second parameter
$image_extension = image_type_to_extension($image_type, true);
// Create a unique image name
$random_image_name = bin2hex(random_bytes(16)) . $image_extension;
/* DEFINE LOCATION */
// 1st create adress with new img random name
$relative_location = "/bazar/assets/img/photos/".$random_image_name;
$absolute_location = dirname(__DIR__, 2).$relative_location;
//var_dump($image_file["tmp_name"][$i]);
//var_dump($absolute_location);
/* MOVE TEMP IMG TO PHOTOS FOLDER */
// 2nd move img with tmp_name to new location under new random name added from 1st step
if (move_uploaded_file($image_file["tmp_name"][$i], $absolute_location)) {
$item_id = 1;
$src = $relative_location;
$query = "INSERT INTO photos (item_id, file_name) VALUES (?, ?)";
$inserted = $db_handler->runQuery( $query, 'is', array($item_id, $src) );
var_dump($inserted);
//$return_arr .= array("name" => $random_image_name,"size" => $image_size, "src"=> $src);
}
}
}
//echo json_encode($return_arr);
Here is code from one Web Source:
$(document).ready(function()
{
$("#drop-area").on('dragenter', function (e){
e.preventDefault();
$(this).css('background', '#BBD5B8');
});
$("#drop-area").on('dragover', function (e){
e.preventDefault();
});
$("#drop-area").on('drop', function (e){
$(this).css('background', '#D8F9D3');
e.preventDefault();
var image = e.originalEvent.dataTransfer.files;
createFormData(image);
});
});
function createFormData(image)
{
var formImage = new FormData();
formImage.append('userImage', image[0]);
uploadFormData(formImage);
}
function uploadFormData(formData)
{
$.ajax({
url: "upload_image.php",
type: "POST",
data: formData,
contentType:false,
cache: false,
processData: false,
success: function(data){
$('#drop-area').html(data);
}});
}
Related
I'm using a script which resizes images client side before uploading them via Ajax to a PHP file. I am resizing images client side to reduce the power demand on the server. My script works but is taking too long to upload the image and I need help understanding why this is the case. The first part of the file asynchronously resizes the image. The second part encodes the resized image source to dynamically created hidden input and finally, Ajax uploads the dynamically created input value to a PHP file by using a timeout 3-second delay function.
$(document).ready(function() {
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
/* Upload Logo Image */
$("#loader-wrapper").hide();
$("#logo_image").change(function(){
$("#ImgText").fadeOut(10, "linear");
$("#loader-wrapper").css('background-image', 'none').fadeIn(200, "linear");
const form = document.querySelector('#user_update_settings');
form.addEventListener('change', async (e) => {
e.preventDefault();
// Get data URI of the selected image
const formData = new FormData(e.currentTarget);
const photoField = formData.get('logo_image');
const dataUri = await dataUriFromFormField(photoField);
// Defines Resized image URL
const imgEl = document.createElement('img');
imgEl.addEventListener('load', () => {
const resizedDataUri = resizeImage(imgEl, 600);
// Deletes Previous Input
var element = document.getElementById('new_logo_image');
if (typeof(element) != 'undefined' && element != null)
{
var elementExists = document.getElementById("new_logo_image");
elementExists.remove();
}
// Creates New Input
var objTo = document.getElementById('LogoInputContainer')
var image_input = document.createElement("input");
image_input.setAttribute("name", "new_logo_image");
image_input.setAttribute("id", "new_logo_image");
image_input.setAttribute("type", "hidden");
image_input.setAttribute("value", resizedDataUri);
objTo.appendChild(image_input);
});
imgEl.src = dataUri;
});
// Resize Script
function dataUriFromFormField (field) {
return new Promise((resolve) => {
const reader = new FileReader();
reader.addEventListener('load', () => {
resolve(reader.result);
});
reader.readAsDataURL(field);
});
}
function resizeImage(imgEl, wantedWidth) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const aspect = imgEl.width / imgEl.height;
canvas.width = wantedWidth;
canvas.height = wantedWidth / aspect;
ctx.drawImage(imgEl, 0, 0, canvas.width, canvas.height);
return canvas.toDataURL();
}
});
// Image Upload
$("#logo_image").change(function(){
setTimeout(() => {
if(window.File && window.FileReader && window.FileList && window.Blob){
if($(this).val()){
// Checks File Extension
var fileExtension = ['jpeg', 'jpg', 'png', 'gif', 'bmp'];
if ($.inArray($(this).val().split('.').pop().toLowerCase(), fileExtension) == -1) {
alert("Only formats are allowed : "+fileExtension.join(', '));
exit();
}
$.ajax({
url: "/logo_uploader",
type: "POST",
data: new FormData($("#user_update_settings")[0]),
contentType: false,
cache: false,
processData: false,
dataType: 'json',
success:function(response){
if(response.type == 'success'){
$("#loader-wrapper").fadeOut();
$(".logo_image_container").css("background-image", "url("+ response.logo_image +")");
}else{
$("#loader-wrapper").fadeOut();
$("#ImgText").fadein(10, "linear").text("+ response.msg +");
}
}
});
}
} else {
alert("Can't upload! Your browser does not support File API!</div>");
return false;
}
}, 3000);
});
});
Client-side, my logo uploader validates the file type, deletes the previous image [if it exists] decodes the $request->new_logo_image and saves the file in the folder 'logos' and in a SQL database.
// User ID
$user_id = auth()->user()->id;
// Reserved Goods
$user = Users::where('id', $user_id)->first();
// Path
$FilePathDB = FilePathDB::first();
// Path
$file_path = $FilePathDB->public_img_path;
$path = $file_path.$user->logo_image;
// Deletes Previous Logo [If Applicable]
if(file_exists($path)) {
File::delete($path);
}
// Validates Extension
$validatedData = $request->validate([
'logo_image' => 'required|image|mimes:jpg,png,jpeg',
]);
// Generates a Unique New Name
$new_name = 'logos/'.$user_id.trim($user->username).uniqid().'.jpg';
// Uploads request Image if file does not exist
if($request->hasFile('logo_image')) {
$image = $request->new_logo_image; // your base64 encoded
$image = str_replace('data:image/png;base64,', '', $image);
$image = str_replace(' ', '+', $image);
File::put($file_path.$new_name, base64_decode($image));
// Logo Image Variable
$formFields = array(
'logo_image' => $new_name
);
// User ID Update
$users = Users::where('id', $user_id)
->update($formFields);
// If Successful
$response = print json_encode(array(
'type'=> 'success',
'msg' => 'Logo Uploaded Successfully',
'logo_image' => 'storage/'.$new_name,
));
} else {
// If unsuccessful
$response = print json_encode(array(
'type'=> 'failed',
'msg' => 'Ooops, image upload unsuccessful. Please try again!',
));
}
Condensed HTML
<form method="POST" action="/user_update_settings" id="user_update_settings" enctype="multipart/form-data">
<label for="logo_image" class="pointer UploadImgBtn transition">
<input class="DisplayNone" type="file" name="logo_image" id="logo_image" accept="image/*">
<a>Upload Image</a>
</label>
</form>
On average, for a 3MB image, it takes 3 seconds for JS canvas to resize the image and 15-20 seconds to upload the already resized file - now between 40-50KB in size. Knowing the image has been resized, why does it take so long for my code to upload the resized image? Are there any loops I haven't considered running in the background which are consuming the client's resources?
After much thinking, I have just found the solution. When I ran my Ajax script I was submitting the whole form to the server which included both the old and the new image. To reduce the data load, one can either put the hidden input into a new form or else specify the data which is to be sent to the PHP file.
Following is my code which upload files on server using dropzone.js plugin:
var file_up_names = new Array;
var duplicate_files = new Array;
var token = $('input[name=_token]').val();
Dropzone.autoDiscover = false;
var dropzone = $("#addPhotosForm").dropzone({
addRemoveLinks: true,
dictRemoveFileConfirmation: "Do you want to remove this image?",
dictDefaultMessage: "Drop images here to upload",
dictRemoveFile: "Remove photo",
init: function() {
this.on("success", function(file, response) {
if (response.status === 1) {
file_up_names.push(response.filename);
$(file.previewTemplate).append('<span class="server_file_path hide">' + response.newfilename + '</span>');
} else if (response.status === 2) {
duplicate_files.push(response.filename);
this.removeFile(file);
}
}),
this.on("queuecomplete", function() {
var html = "Photos added successfully!";
$('#photoUploadSuccess').html('');
$('#photoUploadError').html('');
$('#photoUploadSuccess').removeClass('hide');
$('#photoUploadError').addClass('hide');
if (file_up_names.length > 0) {
if (duplicate_files.length > 0) {
html += " Following photos are skipped as those are already uploaded.";
html += "<ul>";
for (var i = 0; i < duplicate_files.length; ++i) {
html += "<li>";
html += duplicate_files[i];
html += "</li>";
}
html += "</ul>";
}
$('#photoUploadSuccess').html(html);
} else if (duplicate_files.length > 0 && file_up_names.length === 0) {
html = "Following photos already exists. Please check to see if it already exists and try again.";
html += "<ul>";
for (var i = 0; i < duplicate_files.length; ++i) {
html += "<li>";
html += duplicate_files[i];
html += "</li>";
}
html += "</ul>";
$('#photoUploadSuccess').addClass('hide');
$('#photoUploadError').removeClass('hide');
$('#photoUploadError').html(html);
} else {
html = "Photos not uploaded!";
$('#photoUploadSuccess').addClass('hide');
$('#photoUploadError').removeClass('hide');
$('#photoUploadError').html(html);
}
duplicate_files = [];
file_up_names = [];
setTimeout(function() {
$('#photoUploadSuccess').html('');
$('#photoUploadError').html('');
$('#photoUploadSuccess').addClass('hide');
$('#photoUploadError').addClass('hide');
}, 5000);
}),
this.on("removedfile", function(file) {
var server_file = $(file.previewTemplate).children('.server_file_path').text();
// Do a post request and pass this path and use server-side language to delete the file
var token = $('input[name=_token]').val();
$.ajax({
type: 'POST',
headers: {'X-CSRF-TOKEN': token},
url: "{{ URL::route('removePhotos') }}",
data: "file_name=" + server_file,
dataType: 'html'
});
})
}
});
While below is my server code which get file's md5 and store it on server and next time when user upload same image again it check in database and if same md5_file result found it won't allow to upload image. It work when i use simple form, but on dropzone it's not working:
$tempFile = $_FILES['file']['tmp_name'];
$md5_check = md5_file($tempFile);
//check if same image uploaded before
$duplicateCheck = Photos::where('userId', '=', $this->userId)->where('md5_check', '=', "$md5_check")->where('isDeleted', '=', 0)->count();
I've found that if I upload same images together it won't work and return count 0 from DB. But if I upload same images separately like for example upload image once it uploads, upload same image again it get count from DB and give message that image already there. Maybe it's due to dropzone asynchronous calls but can't figure it out how to handle this.
It's because when you upload more than one image $_FILES contains an array of files, so you should cycle it with a foreach and do your operations inside that loop.
You can check what's happening when you upload more than 1 files by inserting this line of code:
var_dump($_FILES);
Or if you want a result better readable:
echo "<pre>".print_r($_FILES, true)."</pre>";
Check the structure of $_FILES when you upload more than 1 file, it should be an array of files.
I am trying to use the JavaScript/jQuery Ajax File Uploader by Jordan Feldstein available at https://github.com/jfeldstein/jQuery.AjaxFileUpload.js
It is an older library but still very popular due to it's simplicity and for being so lightweight (around 100 lines) and you can attach it to a single form input filed and it simply works! You select a file as normal with the form inut filed and on selection it instantly uploads using AJAX and returns the uploaded file URL.
This makes the library good for my use where I am uploading a file inside a modal window which is also generate with AJAX and I have used this library in many similar projects.
My backend is using PHP and Laravel and that is where my issue seems to be.
My test script works but when I implement it into my Laravel app it returns this error....
ERROR: Failed to write data to 1439150550.jpg, check permissions
This error is set in my controller below when this code is not retuning a value...
$result = file_put_contents( $folder . '/' .$filename, file_get_contents('php://input') );
So perhaps this part file_get_contents('php://input') does not contain my file data?
It does create the proper directory structure and even a file which is /uploads/backing/2015/08/1439150550.jpg
The 1439150550.jpg is a timestamp of when the upload took place. It create this file in the proper location however the file created has no content and is 0 bytes!
Below is my Laravel Controller action which handles the back-end upload and below that the JavaScript....
PHP Laravel Controller Method:
public function uploadBackingStageOneFile(){
// Only accept files with these extensions
$whitelist = array('ai', 'psd', 'svg', 'jpg', 'jpeg', 'png', 'gif');
$name = null;
$error = 'No file uploaded.';
$destination = '';
//DIRECTORY_SEPARATOR
$utc_str = gmdate("M d Y H:i:s", time());
$utc = strtotime($utc_str);
$filename = $utc . '.jpg';
$folder = 'uploads/backing/'.date('Y') .'/'.date('m');
//if Directory does not exist, create it
if(! File::isDirectory($folder)){
File::makeDirectory($folder, 0777, true);
}
// Save Image to folder
$result = file_put_contents( $folder . '/' .$filename, file_get_contents('php://input') );
if (!$result) {
Log::info("ERROR: Failed to write data to $filename, check permissions");
return "ERROR: Failed to write data to $filename, check permissions\n";
}
$url = $folder . '/' . $filename;
return Response::json(array(
'name' => $name,
'error' => $error,
'destination' => $url
));
}
JavaScript AJAX FIle Upload LIbrary
/*
// jQuery Ajax File Uploader
//
// #author: Jordan Feldstein <jfeldstein.com>
// https://github.com/jfeldstein/jQuery.AjaxFileUpload.js
// - Ajaxifies an individual <input type="file">
// - Files are sandboxed. Doesn't matter how many, or where they are, on the page.
// - Allows for extra parameters to be included with the file
// - onStart callback can cancel the upload by returning false
Demo HTML upload input
<input id="new-backing-stage-1-file" type="file">
Demo JavaScript to setup/init this lbrary on the upload field
$('#new-backing-stage-1-file').ajaxfileupload({
'action': '/upload.php',
'params': {
'extra': 'info'
},
'onComplete': function(response) {
console.log('custom handler for file:');
alert(JSON.stringify(response));
},
'onStart': function() {
if(weWantedTo) return false; // cancels upload
},
'onCancel': function() {
console.log('no file selected');
}
});
*/
(function($) {
$.fn.ajaxfileupload = function(options) {
var settings = {
params: {},
action: '',
onStart: function() { },
onComplete: function(response) { },
onCancel: function() { },
validate_extensions : true,
valid_extensions : ['gif','png','jpg','jpeg'],
submit_button : null
};
var uploading_file = false;
if ( options ) {
$.extend( settings, options );
}
// 'this' is a jQuery collection of one or more (hopefully)
// file elements, but doesn't check for this yet
return this.each(function() {
var $element = $(this);
// Skip elements that are already setup. May replace this
// with uninit() later, to allow updating that settings
if($element.data('ajaxUploader-setup') === true) return;
$element.change(function()
{
// since a new image was selected, reset the marker
uploading_file = false;
// only update the file from here if we haven't assigned a submit button
if (settings.submit_button == null)
{
upload_file();
}
});
if (settings.submit_button == null)
{
// do nothing
} else
{
settings.submit_button.click(function(e)
{
// Prevent non-AJAXy submit
e.preventDefault();
// only attempt to upload file if we're not uploading
if (!uploading_file)
{
upload_file();
}
});
}
var upload_file = function()
{
if($element.val() == '') return settings.onCancel.apply($element, [settings.params]);
// make sure extension is valid
var ext = $element.val().split('.').pop().toLowerCase();
if(true === settings.validate_extensions && $.inArray(ext, settings.valid_extensions) == -1)
{
// Pass back to the user
settings.onComplete.apply($element, [{status: false, message: 'The select file type is invalid. File must be ' + settings.valid_extensions.join(', ') + '.'}, settings.params]);
} else
{
uploading_file = true;
// Creates the form, extra inputs and iframe used to
// submit / upload the file
wrapElement($element);
// Call user-supplied (or default) onStart(), setting
// it's this context to the file DOM element
var ret = settings.onStart.apply($element, [settings.params]);
// let onStart have the option to cancel the upload
if(ret !== false)
{
$element.parent('form').submit(function(e) { e.stopPropagation(); }).submit();
}
}
};
// Mark this element as setup
$element.data('ajaxUploader-setup', true);
/*
// Internal handler that tries to parse the response
// and clean up after ourselves.
*/
var handleResponse = function(loadedFrame, element) {
var response, responseStr = $(loadedFrame).contents().text();
try {
//response = $.parseJSON($.trim(responseStr));
response = JSON.parse(responseStr);
} catch(e) {
response = responseStr;
}
// Tear-down the wrapper form
element.siblings().remove();
element.unwrap();
uploading_file = false;
// Pass back to the user
settings.onComplete.apply(element, [response, settings.params]);
};
/*
// Wraps element in a <form> tag, and inserts hidden inputs for each
// key:value pair in settings.params so they can be sent along with
// the upload. Then, creates an iframe that the whole thing is
// uploaded through.
*/
var wrapElement = function(element) {
// Create an iframe to submit through, using a semi-unique ID
var frame_id = 'ajaxUploader-iframe-' + Math.round(new Date().getTime() / 1000)
$('body').after('<iframe width="0" height="0" style="display:none;" name="'+frame_id+'" id="'+frame_id+'"/>');
$('#'+frame_id).get(0).onload = function() {
handleResponse(this, element);
};
// Wrap it in a form
element.wrap(function() {
return '<form action="' + settings.action + '" method="POST" enctype="multipart/form-data" target="'+frame_id+'" />'
})
// Insert <input type='hidden'>'s for each param
.before(function() {
var key, html = '';
for(key in settings.params) {
var paramVal = settings.params[key];
if (typeof paramVal === 'function') {
paramVal = paramVal();
}
html += '<input type="hidden" name="' + key + '" value="' + paramVal + '" />';
}
return html;
});
}
});
}
})( jQuery );
My JavaScript usage of the above library:
// When Modal is shown, init the AJAX uploader library
$("#orderModal").on('shown.bs.modal', function () {
// upload new backing file
$('#new-backing-stage-1-file').ajaxfileupload({
action: 'http://timeclock.hgjghjg.com/orders/orderboards/order/uploadbackingimage',
params: {
extra: 'info'
},
onComplete: function(response) {
console.log('custom handler for file:');
console.log('got response: ');
console.log(response);
console.log(this);
//alert(JSON.stringify(response));
},
onStart: function() {
//if(weWantedTo) return false; // cancels upload
console.log('starting upload');
console.log(this);
},
onCancel: function() {
console.log('no file selected');
console.log('cancelling: ');
console.log(this);
}
});
});
The problem is like you said, file_get_contents('php://input') does not contain your file data.
jQuery.AjaxFileUpload plugin wrap file input element with a form element that contains enctype="multipart/form-data" attribute. 1
From php documentation: 2
php://input is not available with enctype="multipart/form-data".
Here is my js, and action
public function postyAction()
{
$this->_helper->viewRenderer->setNoRender(TRUE);
// list of valid extensions, ex. array("jpeg", "xml", "bmp")
$allowedExtensions = array("jpeg","xlsx", "jpg", "gif", "doc", "docx", "xls", "odt", "pdf");
// max file size in bytes
$sizeLimit = 6 * 1024 * 1024;
$f = '../dir/'; //dir to save files
$uploader = new qqFileUploader($allowedExtensions, $sizeLimit);
$result = $uploader->handleUpload($f);
// to pass data through iframe you will need to encode all html tags
echo htmlspecialchars(json_encode($result), ENT_NOQUOTES);
}
function createUploader(){
var UploadedFileNames = [];
var uploader = new qq.FileUploader({
element: document.getElementById('file-uploader-demo1'),
action: '../orders/posty',
sizeLimit: 10000000, //10MB maximo
debug:false,
onComplete: function(id, fileName, responseJson){
var success = responseJson["success"];
if(success == true){
var files = $(".qq-upload-file").toArray();
var UploadedFileNames = [];
for ( var i = 0; i < files.length; i++ ) {
UploadedFileNames.push( files[ i ].innerHTML );
}
$( "#filenem" ).val( UploadedFileNames.join( "#" ) );
}
//$("#filenem").val(fileName);
}
});
}
// in your app create uploader as soon as the DOM is ready
// don't wait for the window to load
window.onload = createUploader;
I wish to get an array of files onComplete(), so that i can insert them into db. my code gets the inner html of the file list but longer file names appear to be dotted hence cant get the real file name. eg . e"myverylongdocume..now.doc" instead of "myverylongdocumentnow.doc" for instance
I've created a functionality on my website where user's can change the background image via upload. The procedure is following:
User goes to settings page and selects an image file to be uploaded. After selecting image, the browser will output it so that user can preview
it before actually saving it's file to in to the folder and filepath in to the database. After that, if user is happy with the result, he can save it to the
folder by pressing "Upload Background Image" button.
All of the above is handled with AJAX.
I am having trouble to just output the image to the browser without actually saving it twice, first into tests folder and after that into backgrounds folder.
I'm using CodeIgniter as my backend framework and jQuery for my AJAX requests.
Here are my methods for outputting (testing) and saving the image:
public function test_image()
{
if($this->input->is_ajax_request())
{
// This part of code needs to be replaced to only just output the image (return it as a JSON), not actually saving it to another a folder
$ext = pathinfo($_FILES['userfile']['name'], PATHINFO_EXTENSION);
$new_img_name = random_string('unique'). "." . $ext;
$config['upload_path'] = './public/images/uploads/tests';
$config['allowed_types'] = 'gif|jpg|jpeg|png';
$config['max_size'] = '1000000';
$config['max_width'] = '2000';
$config['max_height'] = '1600';
$config['file_name'] = $new_img_name;
$this->load->library('upload', $config);
if (!$this->upload->do_upload()) {
$this->output->set_content_type('application_json');
$this->output->set_output(json_encode(array('image_errors' => $this->upload->display_errors('<p class="text-center">','</p>'))));
return false;
} else {
$this->output->set_content_type('application_json');
$this->output->set_output(json_encode(array('userfile' => $new_img_name)));
}
} else {
echo "Not an ajax request";
}
}
// This method works properly
public function upload_background_image()
{
if (isset($_POST))
{
$ext = pathinfo($_FILES['userfile']['name'], PATHINFO_EXTENSION);
$new_img_name = random_string('unique'). "." . $ext;
$config['upload_path'] = './public/images/uploads/backgrounds';
$config['allowed_types'] = 'gif|jpg|jpeg|png';
$config['max_size'] = '1000000';
$config['max_width'] = '2000';
$config['max_height'] = '1600';
$config['file_name'] = $new_img_name;
$this->load->library('upload', $config);
if (!$this->upload->do_upload()) {
$this->output->set_content_type('application_json');
$this->output->set_output(json_encode(array('image_errors' => $this->upload->display_errors('<p class="text-center">','</p>'))));
return false;
} else {
$this->load->model('user_model');
$user_id = $this->session->userdata('user_id');
$upload_photo = $this->user_model->updateUserInfo($user_id, ['body_background_url' => $new_img_name]);
if ($upload_photo === true) {
$this->session->set_userdata(['body_background_url' => $new_img_name]);
redirect(base_url());
}
}
}
}
And here's my AJAX:
$("#bg-cover-file").change(function(e) {
e.preventDefault();
var form = $(this).closest('form');
form.ajaxSubmit({
dataType: 'json',
beforeSubmit: function() {
},
success: function(response) {
if(response.userfile) {
// Output the image
$('.test-image').attr('src', response.userfile);
$('span.file-input').hide();
// Change the form action attribute
var new_path = 'uploads/upload_background_image';
form.attr('action', new_path);
} else {
$('#error-modal').modal('show');
$("#error-body").html(response.image_errors);
return false;
}
}
});
return false;
});
--Working Demo--
I have put comments in this demo to explain what the steps are so please read them.
If you don't understand anything in this answer please leave a comment below and i will update the answer until you understand line for line. You don't learn from copy/paste so please be sure to understand the answer.
function MyFunction() {
var img=document.getElementById('BackgroundImage');
var Status=document.getElementById('Status');
var savebtn=document.getElementById('savebtn');
/* SetBG will target the body tag of the web page.
You can change this to any element -
var SetBG=document.getElementById('YourID').style;
*/
var SetBG=document.body.style;
//Split the image name
var fileExt=img.value.split('.');
//Use the last array from the split and put to lowercase
var fileformat=fileExt[fileExt.length -1].toLowerCase();
// Check the file extension (Image formats only!)
if((fileformat==='jpg')||(fileformat==='gif')||(fileformat==='png')||(fileformat==='jpeg')) {
if (img.files && img.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
//----Image is ready for preview.
SetBG.background='url('+e.target.result+') no-repeat center center fixed';
/*---- Optional, Set background as cover ---*/
SetBG.backgroundSize="cover";
SetBG.OBackgroundSize="cover";
SetBG.webkitBackgroundSize="cover";
//--Hide Loading Message
Status.style.display="none";
//----- Display (Save/Upload button?)
savebtn.style.display="block";
}
/*-------Reading File....
Display a message or loading gif for large images to be processed?
*/
Status.innerHTML="Loading...";
Status.style.display="block";
savebtn.style.display="none";
reader.readAsDataURL(img.files[0]);
}
}else{
/*----User file input not accepted (File isn't jpg/gif/png/jpeg)
Empty the input element and set the background to default.
*/
Status.innerHTML="Format not accepted";
Status.style.display="block";
savebtn.style.display="none";
SetBG.background='white';
document.getElementById('BackgroundImage').value='';
}
}
#Status{display:none;background:white;color:black;font-size:16pt;}
#savebtn{display:none;}
<div id="Status"></div>
<input type="file" id="BackgroundImage" onchange="MyFunction()"/>
<button id="savebtn" onclick="alert('Now upload the image');">Upload and save</button>
I hope this helps. Happy coding!
This may help you
let assume your browse button's id is bg-cover-file and the id of the image tag where you want to display the image preview_image
$(document).on("change", "#bg-cover-file", function(event)
{
if (this.files && this.files[0])
{
var reader = new FileReader();
reader.onload = function (e)
{
$('#preview_image').attr('src', e.target.result);
}
reader.readAsDataURL(this.files[0]);
}
});
function MyFunction() {
var img=document.getElementById('BackgroundImage');
var Status=document.getElementById('Status');
var savebtn=document.getElementById('savebtn');
/* SetBG will target the body tag of the web page.
You can change this to any element -
var SetBG=document.getElementById('YourID').style;
*/
var SetBG=document.body.style;
//Split the image name
var fileExt=img.value.split('.');
//Use the last array from the split and put to lowercase
var fileformat=fileExt[fileExt.length -1].toLowerCase();
// Check the file extension (Image formats only!)
if((fileformat==='jpg')||(fileformat==='gif')||(fileformat==='png')||(fileformat==='jpeg')) {
if (img.files && img.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
//----Image is ready for preview.
SetBG.background='url('+e.target.result+') no-repeat center center fixed';
/*---- Optional, Set background as cover ---*/
SetBG.backgroundSize="cover";
SetBG.OBackgroundSize="cover";
SetBG.webkitBackgroundSize="cover";
//--Hide Loading Message
Status.style.display="none";
//----- Display (Save/Upload button?)
savebtn.style.display="block";
}
/*-------Reading File....
Display a message or loading gif for large images to be processed?
*/
Status.innerHTML="Loading...";
Status.style.display="block";
savebtn.style.display="none";
reader.readAsDataURL(img.files[0]);
}
}else{
/*----User file input not accepted (File isn't jpg/gif/png/jpeg)
Empty the input element and set the background to default.
*/
Status.innerHTML="Format not accepted";
Status.style.display="block";
savebtn.style.display="none";
SetBG.background='white';
document.getElementById('BackgroundImage').value='';
}
}
#Status{display:none;background:white;color:black;font-size:16pt;}
#savebtn{display:none;}
<div id="Status"></div>
<input type="file" id="BackgroundImage" onchange="MyFunction()"/>
<button id="savebtn" onclick="alert('Now upload the image');">Upload and save</button>