I am trying to create a simple jquery file upload following the steps in this tutorial: http://net.tutsplus.com/tutorials/javascript-ajax/uploading-files-with-ajax/ and it is fully functional and excellent. However I want to have more control and more security over what users can upload images, for example I want to be able to send a token as well. Here is my original code:
$('#images').change(function (evt) {
$('#response').text("Uploading . . .");
var amount = this.files.length;
var reader, file;
alert(formdata);
for (var i = 0; i < amount; i++ ) {
file = this.files[i];
if (!!file.type.match(/image.*/)) {
if ( window.FileReader ) {
reader = new FileReader();
reader.onloadend = function (e) {
appendUploadedPic(e.target.result, file.fileName);
};
reader.readAsDataURL(file);
}
if (formdata) {
formdata.append("images[]", file);
}
}
}
if (formdata) {
$.ajax({
url: "php/upload.php",
type: "POST",
data: formdata,
dataType:'json',
processData: false,
contentType: false,
success: function (res) {
$('#response').html(res['image_name']);
}
});
}
});
and the php is simple so far
foreach ($_FILES["images"]["error"] as $key => $error) {
if ($error == UPLOAD_ERR_OK) {
$name = $_FILES["images"]["name"][$key];
move_uploaded_file( $_FILES["images"]["tmp_name"][$key], "../members/" . $_FILES['images']['name'][$key]);
}
}
$respond = array("filename" => $_FILES["images"]["name"][0], "type" => "image");
echo json_encode($respond);
now what I want to do is this
$.ajax({
url: "php/upload.php",
type: "POST",
data: 'token=someToken&code=upload&data='+formdata,
dataType:'json',
processData: false,
contentType: false,
success: function (res) {
$('#response').html(res);
}
});
Is it possible? If so, how would I read it from php? It its a post method i normally use a $_POST['code'] and $_POST'token'], but how would I read the images formdata object?
If formdata was created with FormData, you append additional fields like this:
formdata.append(name, value);
So, if you want to add a token field and a code field, it's:
if (formdata) {
formdata.append("images[]", file);
formdata.append("token", "someToken");
formdata.append("code", "someCode");
}
However, you should be aware that this method of uploading won't be supported by a lot of browsers, namely IE6-IE9, which represents about half of all internet users, I believe.
I recommend using a plugin that detects browser support and uses the best available method. I wrote this plugin for exactly that purpose:
https://github.com/LPology/Simple-Ajax-Uploader
There's a link to a demo at the top to try it out. If you decide to use it and run into any issues, just ping me and I can help you out. Good luck.
Related
What changes, please, can I make to the code below, so that my upload.php file can receive multiple files which I would them loop through to process? I have been trying to upload multiple files at once to server using Summernote. Although, I select multiple files from my laptop for the upload, only one file gets uploaded. I feel certain that the PHP file which handles the upload is not the problem because it receives only one file even if I select multiple files for upload. Below is how the JQuery code looks like
$('.summernote-mini').summernote({
height: 200,
tabsize: 2,
callbacks: {
onFileUpload: function(files) {
callBack(files[0]);
},
}
});
Callback Function
function callBack(files) {
let data = new FormData();
data.append('media_upload[]', files);
$.ajax({
data: data,
type: "POST",
url: "upload.php",
cache: false,
contentType: false,
processData: false,
xhr: function() { //Handle progress upload
let myXhr = $.ajaxSettings.xhr();
if (myXhr.upload) myXhr.upload.addEventListener('progress', progressHandlingFunction, false);
return myXhr;
}
}).done(function(reponse){
//I handle the response here
});
}
Regardless of the number of files I select to upload, count($_FILES['media_upload']['name']) gives me 1 in my e.g upload.php file which handles the server side file upload. What could be the solution to this problem?
After receiving assistance from #Fravadona I got the solution to the problem.
I replaced callBack(files[0]); with callBack(files);as #Fravadona suggested.
Then, in the call back function, I replaced data.append('media_upload[]', files); with the code below:
var iLength = files.length;
var i;
for(i = 0; i < iLength; i++){
data.append("media_upload[]", files[i]);
}
So the correct code has become this:
$('.summernote-mini').summernote({
height: 200,
tabsize: 2,
callbacks: {
onFileUpload: function(files) {
callBack(files);
},
}
});
And the call back function has become this:
function callBack(files) {
let data = new FormData();
var iLength = files.length;
var i;
for(i = 0; i < iLength; i++){
data.append("media_upload[]", files[i]);
}
$.ajax({
data: data,
type: "POST",
url: "upload.php",
cache: false,
contentType: false,
processData: false,
xhr: function() { //Handle progress upload
let myXhr = $.ajaxSettings.xhr();
if (myXhr.upload) myXhr.upload.addEventListener('progress', progressHandlingFunction, false);
return myXhr;
}
}).done(function(reponse){
//I handle the response here
});
}
So now, I am able to successfully upload multiple files at once using Summmernote.
I've recently used lots of Ajax methods in one of my projects, since in every $.ajax call you have to write many of the same codes, like:
{
type:'POST', // Default value is 'GET'
beforeSend: function(xhr){
// Usually do some Page Loading Animation stuff here
},
error:function(){
// Handling the Exceptions here
}
}
So I've encapsulated the Ajax call into a class, called JAjax, like this :
(function ($) {
// More details, see: http://api.jquery.com/jquery.ajax/
var defaults = {
data: {},
url: '',
type: 'POST',
dataType: 'JSON',
isOverlay: true,
async: true,
cache: true,
contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
processData: true,
overlayTarget: $(document.body),
dealyClean: 500,
f_before: function () {},
f_done: function () { },
f_always: function () { },
f_fail: function (xhr) {
if (xhr.status !== 200) {
// Handling the Exceptions
}
}
};
function JAjax(_options) {
this.options = $.extend({}, defaults, _options);
this.execute();
}
function createOverLayer(options) {
// Create a page loading animation layer
}
JAjax.prototype = {
execute: function () {
var parent = this;
var response = $.ajax({
data: parent.options.data,
url: parent.options.url,
type: parent.options.type,
dataType: parent.options.dataType,
contentType: parent.options.contentType,
async: parent.options.async,
cache: parent.options.cache,
processData: parent.options.processData,
beforeSend: function (xhr) {
parent.options.f_before();
if (parent.options.isOverlay) {
createOverLayer(parent.options);
}
}
});
response.done(parent.options.f_done);
response.always(parent.options.f_always);
response.fail(parent.options.f_fail);
}
};
jQuery.extend({
jajax: function (_options) {
_options = _options || {};
if (typeof _options === 'object') {
return new JAjax(_options);
}
}
});
})(jQuery);
For most Ajax requests (GET, POST), it works fine. But when I use it to upload some files, The file will successfully upload to the server and back to me a filename(string) as an execution result. But somehow, it doesn't trigger the f_done function, below is how I use it to upload the files:
var url = '/ajax_common_file_upload';
var file_data = new FormData();
for (var i = 0; i < _files.length; i++) {
var file = _files[i];
file_data.append('input_files[]', file);
}
$.jajax({
url: url,
data: file_data,
cache: false,
contentType: false,
processData: false,
f_done: function (result) {
// Never to be executed :-(
alert('show me something, please!');
}
});
I spend days to try to figure it out why it doesn't 'SHOW ME SOMETHING' but all failed, will be very appreciated that someone can help me out and explain why the f_done() method cannot be triggered when I use it to upload files.
Update:
I made some screenshots for both JAjax and original $.ajax on Request Headers and merge them together like below:
I used the same parameters to make the request for both JAjax and $.ajax, but I don't know why they have a different Accept value!
ANYONE?
Still can not trigger the f_done() function!!! but since I can do the same thing at f_always(), I'm gonna skip this and moving on. I will keep this post open and always appreciate for any suggestions!
I am trying to build off a question I asked yesterday.
I am able to pass the file over to PHP using the ajax method. But I need to be able to change the file name to the booking number. For some reason, the booking was not being passed over to the PHP script. So I attempted the following:
$('#uploadBtn').on('click', function()
{
var form_data = new FormData();
form_data.append("file", document.getElementById('pdfFile').files[0]);
var booking = $('#bookingNum').val();
var partner = $('#partnerCode').val();
$.post('process/fileUpload.php', {booking:booking, partner:partner}, function(data)
{
// Wasn't sure if I needed anything here
console.log(data);
});
$.ajax({
url: 'process/fileUpload.php',
method:"POST",
data: form_data,
contentType: false,
cache: false,
processData: false,
success: function(data){console.log(data);},
error: function(jqHHR, textStatus, errorThrown){console.log('fail: ' + errorThrown);}
});
});
As you will notice above, I had to use the $.post method to send the booking and partner over to the php script.
I then used $.ajax to send the form_data over to the same script.
(I could not achieve this in one motion from yesterday's question I asked. So this is my second attempt to complete this. If there is a way to send all of the info in one motion, please refer to the question I linked above.)
So over in the PHP script, I am able to get all of the items I needed with a couple of functions:
<?php
// from the $.post method
if(isset($_POST['booking']))
{
$booking = $_POST['booking'];
$partner = $_POST['partner'];
getInfo($booking);
}
// from the $.ajax method
if($_FILES['file'])
{
$file = var_dump($_FILES['file']);
getFile($file);
}
function getInfo($booking)
{
return $booking;
}
function getFile($file)
{
return $file;
}
?>
I know it's not pretty, but I am able to get the booking (I don't need the partner right now), and I am also able to get the file information.
What I need to do is rename the file to the booking, and then finally upload it to the necessary directory.
I wasn't sure if I had to combine the functions, but I did try to no avail.
With that said, I am able to get the booking and file info within the PHP script. Now how would I go about renaming the file to the booking?
As you used form_data.append() to add the file data to the formdata. did it not occur to you to also use that to add the booking and partner values to it as well?
$('#uploadBtn').on('click', function()
{
var form_data = new FormData();
form_data.append("file", document.getElementById('pdfFile').files[0]);
form_data.append('booking', $('#bookingNum').val());
form_data.append('partner', $('#partnerCode').val());
$.post('process/fileUpload.php', form_data, function(data)
{
console.log(data);
});
});
To fix your ajax request (especially the illegal invocation), use the following javascript code
$('#uploadBtn').on('click', function()
{
var form_data = new FormData();
form_data.append("file", document.getElementById('pdfFile').files[0]);
form_data.append('booking', $('#bookingNum').val());
form_data.append('partner', $('#partnerCode').val());
$.ajax({
type: "POST",
url: 'process/fileUpload.php',
data: form_data,
processData: false,
contentType: false,
success: function(data) { console.log(data); }
});
});
Notice the use of processData: false and contentType: false
I am trying to send an image via JavaScript after displaying the preview on the screen, I refuse to use Dropzone, Plupload or any JavaScript library, because I just want a simple functionality.
var finput;
var fileReader;
var fd; //formdata...
$(function(){
fileReader = new FileReader;
fileReader.onload = function(e) {
var img = document.createElement("img");
img.src = fileReader.result; //display the image on the screen...
img.height = 200;
$('#pimgbox').html(img);
//enable the image button... // $('#pimagebox').html(img);
$('#pimgbtn').removeAttr('disabled', 'disabled').removeClass('btn-alert').addClass('btn-primary');
}
});
and when you click on the submit button
$('#pimgbtn').bind('click', function(e){
//initialize the formdata...
fd = new FormData(); //initialize the formdata..
fd.append('pimgupl', finput.files[0]); //s
//alert(fd);
$('#pimgldr').css('display','block');
$(this).attr('disabled', 'disabled').removeClass('btn-primary').addClass('btn-alert')
if(fileReader.result != null){
////////////// beginning of ajax call...
var settings = {
type: "POST",
url: "/user/setprofilepix",
data: fd
}; //end of setting..
$.ajax(settings)
.done(function (response) {
alert(response);
resetPUplButtons();
//close modal...
$('#profimgwin').modal('hide');
})
.fail(function (jqXHR, textStatus, error) {
alert('error sending file...' + error);
resetPUplButtons();
});
/////////////////////////end of ajax calls..
}else{
alert('you havent selected an image yet..');
resetPUplButtons();
}
});
and on my Laravel usercontroller I have this method that tries to upload the image but it's refusing to work.
public function setProfilePix(Request $request){
$file = $request->file('pimgupl');
$code = Auth::user()->profile->usrcode;
$targetDir = public_path() . DS . 'userdata'.DS.'ppix';
$newname = $targetDir.DS.strtolower($code).md5($code).'_'.time() .'.jpg';
// echo $newname;
//exit;
$file->move($targetDir, $newname);
echo 'saved';
}
I keep getting this error in my output panel:
Line 418 is the ajax operation, I have tried everything but nothing seems to be working. I have also added an exception to the route in my verifyCsrfToken.php - middleware, but still it isn't working.
In your jQuery try changing settings from
var settings = {
type: "POST",
url: "/user/setprofilepix",
data: fd
};
to
var settings = {
type: "POST",
url: "/user/setprofilepix",
data: fd,
processData: false,
contentType: false,
};
I'm trying to upload multiple files from a drag/drop event using jQuery/AJAX/Laravel.
MY DROP EVENT:
$( document ).on('drop dragleave', '.file-drag', function(e){
$(this).removeClass('drop-ready');
if(e.originalEvent.dataTransfer.files.length) {
e.preventDefault();
e.stopPropagation();
if (e.type === "drop") {
var files = e.originalEvent.dataTransfer.files;
AjaxFileUpload(files)
}
}
});
MY UPLOAD SCRIPT:
function AjaxFileUpload(files){
console.log(files);
//Start appending the files to the FormData object.
var formData = new FormData;
formData.append('_token', CSRF_TOKEN);
for(var i = 0; i < files.length; i++){
formData.append(files[i].name, files[i])
}
console.log(formData.entries());
$.ajax({
//Server script/controller to process the upload
url: 'upload',
type: 'POST',
// Form data
data: formData,
// Tell jQuery not to process data or worry about content-type
// You *must* include these options!
cache: false,
contentType: false,
processData: false,
// Error logging
error: function(jqXHR, textStatus, errorThrown){
console.log(JSON.stringify(jqXHR));
console.log('AJAX Error: ' + textStatus + ": " + errorThrown);
},
// Custom XMLHttpRequest
xhr: function() {
var myXhr = $.ajaxSettings.xhr();
if (myXhr.upload) {
// For handling the progress of the upload
myXhr.upload.addEventListener('progress', function(e) {
if (e.lengthComputable) {
$('progress').attr({
value: e.loaded,
max: e.total,
});
}
} , false);
}
return myXhr;
},
success: function(data){
console.log(data);
}
});
}
MY CONTROLLER CODE:
class UploadsController extends Controller
{
public function UploadFiles(Request $request){
return $request->all();
}
}
I THINK my images are getting to the server side, as when I return the request object, I get the following in console:
Thus, the CSRF token is getting through, and the images (I think?) are getting through. My problem from here is accessing the files with PHP and storing them via ->store();.
In the countless examples online/documentation, they typically use something along the lines of:
$path = $request->photo->store('images');
However, I don't understand the 'photo' aspect of this. What if a video or a PDF is uploaded? I basically don't understand how I am to access the different parts of the request object. Documentation on Laravel site is pretty sparse for this and only gives an example using 'photo' of which it never explains.
Figured it out.
In my uploadscontroller:
class UploadsController extends Controller
{
public function UploadFiles(Request $request){
$arr = [];
foreach($request->all() as $file){
if(is_file($file)){
$string = str_random(16);
$ext = $file->guessExtension();
$file_name = $string . '.' . $ext;
$filepath = 'uploads/' . Auth::user()->username . '/' . $file_name;
$file->storeAs(('uploads/' . Auth::user()->username), $file_name);
array_push($arr, [$file_name, $filepath]);
}
}
return $arr;
}
}
This took me a while but I finally got a working solution. I'm using Dropzone so the list of file objects is returned by getAcceptedFiles() but it should be the same concept for you. I'm also attaching the files to an existing form.
Upload:
var formElement = document.getElementById("addForm");
var formData = new FormData(formElement);
// Attach uploaded files to form submission
var files = myDZ.getAcceptedFiles(); // using Dropzone
for (var i = files.length - 1; i >= 0; i--) {
formData.append('files[]', files[i]);
}
$.ajax({
url: 'home/',
data: formData,
processData: false,
contentType: false,
timeout: 1000,
type: 'POST',
headers: {
'X-CSRF-TOKEN': Laravel.csrfToken,
},
success: function(){
...
},
error: function (jqXHR, textStatus) {
...
}
});
Controller:
foreach($request->only('files') as $files){
foreach ($files as $file) {
if(is_file($file)) { // not sure this is needed
$fname = $file->getClientOriginalName();
$fpath = $file->store('docs'); // path to file
}
}
}
Dropzone Script:
Dropzone.autoDiscover = false;
var myDZ = new Dropzone("#my-dropzone", {
url: "/home/files",
maxFilesize: 5,
maxFiles: 5,
addRemoveLinks: true,
dictDefaultMessage: 'Drop files here or click to upload <br> (max: 5 files)',
headers: {
'X-CSRF-TOKEN': Laravel.csrfToken
},
});
Regarding the examples found in Laravel's documentation, 'photo' is simply making use of a magic method to reference a file uploaded with a name of 'photo'. You can replace 'photo' with whatever your specific file names is/are. Specific functions capable of being called on your uploaded files can be found here.