We are taking over a site that is using Angular js for a form for data and multi-file upload (up to 4 files).
We are posting to a .php file. I have been able to separate and capture the field data - but can only seem to 'see' one file. If I upload multiple files I 'see' only the last one added to the form.
Can someone please help me to get the files using PHP?
Form HTML (for files):
<ul class="attachments">
<li ng-repeat="attachment in attachments" class="attachment">
{{attachment.name}}
<i class="btn-submit-att-x-hover-off"></i>
</li>
</ul>
<button type="button" class="attachments-button btn btn-default" ngf-select ngf-change="onFileSelect($files)" ngf-multiple="true" ng-if="attachments.length < 4">
<i class="btn-submit-attch"></i>Upload Attachments
</button>
Angular JS Code - for the files as I see:
$scope.attachments = [];
//onFileSelect adds file attachments to the $scope up to the
//configured maximum value.
$scope.onFileSelect = function ($files) {
var MAX_ATTACHMENTS = 4;
for(var i=0; i < $files.length; i++){
if($scope.attachments.length >= MAX_ATTACHMENTS){
break;
}
$scope.attachments.push($files[i]);
}
};
$scope.removeAttachment = function (attachment) {
var index = $scope.attachments.indexOf(attachment);
if (index > -1) {
$scope.attachments.splice(index, 1);
}
};
//POST form data
$scope.upload = Upload.upload({
url: 'forms/pressrelease.html',
method: 'POST',
data: $scope.model,
file: $scope.attachments,
fileFormDataName: "files"
})
.success(function (data, status, headers, config) {
alertSuccess("Your press release has been submitted successfully!");
$scope.showSuccess = true;
}
The only PHP code we've used that has given us anything back - but again - only 1, or the last file name is:
if(isset($_FILES['files'])){
$files = $_FILES['files'];
$sentfilename = $_FILES['files']['name'];
$email_body .= "I see files: $sentfilename<br />";
$tmpsentfile = $_FILES['files']['tmp_name'];
$i=0;
$getfile = $files[0];
$sentfilename = $getfile;
$sentfilesize = $_FILES['files']['size'];
$errormessage = $_FILES['files']['error'];
$email_body .= "I see files: $sentfilename<br />";
}
If anyone one can show us the PHP code we need to use to capture these uploaded files.
Thanks
Create a names array and post the dynamic file names:
var names = []
foreach file:
names.push(files[i].name)
in upload:
method: 'POST',
data: $scope.model,
file: $scope.attachments,
fileFormDataName: names
Related
I have an input which accepts multiple files:
<input id="propertyImages" type="file" name="submission_img[]" multiple accept=".jpg, .jpeg, .png, .gif"/>
I then POST this data to a PHP file via JS/Ajax:
//Compiling the data which will be POSTed using AJAX. Note that the input above with id of propertyImages is not part of the form with id accommodationForm and is therefore appended to the formData.
var form = $("#accommodationForm")[0];
var formData = new FormData(form);
var propertyImages = document.getElementById("propertyImages");
for(var i=0; i<propertyImages.files.length; i++){
formData.append('propImages[]', propertyImages.files[i]);
}
//Printing to console the formData entries to confirm the formData data:
for (var d of formData.entries()) {
console.log(d);
}
//The partial code for the ajax call:
$.ajax({
url: "includes/handlers/ajax_submit_accommodation.php",
method: "POST",
data: formData,
...
The PHP code to which the data is POSTed:
$errorstatus = 0; //Used as an error flag
$maximagesize = 10485760; // Max file size allowed = 10MB
$acceptableimage = array( //The allowed mime types
'image/jpeg',
'image/jpg',
'image/png',
);
$submission_images_array = array();
$output = ""; //Used for debugging purposes
for($x=0; $x<30; $x++){ //30 is the upper limit for the amount of images POSTed
if($_FILES['propImages']['size'][$x] != 0){
if( $_FILES['propImages']['size'][$x] >= $maximagesize || (!in_array($_FILES['propImages']['type'][$x], $acceptableimage))) {
$errorstatus = 1;
} else {
$submission_img = $_FILES['propImages']['name'][$x];
$submission_img = pathinfo($submission_img, PATHINFO_FILENAME);
$submission_img = $submission_img.'-'.$user_id.'-'.rand().'.jpeg';
array_push($submission_images_array, $submission_img);
$submission_img_tmp = $_FILES['propImages']['tmp_name'][$x];
compressImage($submission_img_tmp, '../../submitted_media/accommodation/'.$submission_img, 50);
$output .= "$x:"." ".$submission_img."\n";
}
} else {
$output .= "$x: Empty image\n";
$submission_img = "";
array_push($submission_images_array, $submission_img);
}
}
file_put_contents('../../filenames.txt', $output ); // DEBUG
I want the user to attach a maximum of 30 images. I have made sure that the front end and back end check for this. The issue I am having is that I can attach 30 images - the debugging confirms this via the printing to console of the formData...i.e. the formData contains all the image data. However, when I look at the filenames.txt which I logged in the PHP it only contains the first 20 file names, while the last 10 entries is logged as "Empty image" (as I instructed in the code if the image size is 0).
Notes:
I have made sure (using other JS code) that propertyImages.files.length in the for loop cannot be greater than 30 and that when I attach 30 images, that the length is indeed 30.
I have made sure that max_input_vars in the PHP.ini file is not the issue.
The image sizes of the last 10 files are NOT 0. This has been confirmed by the logging of formData
Have a look at the max_file_uploads directive:
max_file_uploads integer
The maximum number of files allowed to be uploaded simultaneously.
As shown in the table in the "File Uploads" section, it defaults to 20:
Name Default Changeable Changelog
...
max_file_uploads 20 PHP_INI_SYSTEM Available since PHP 5.2.12.
You'll need to update that value your php.ini.
Also check you don't exceed post_max_size or
upload_max_filesize.
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.
In my page I had only one html file input :
input type="file" name="file" files-model="service.file" id="filo" accept=".zip" />
And I used to get my file to be uploaded in a multi-part file request like this :
var fd = new FormData();
fd.append('file', filo.files[0]);//filo is the id of the input
//and send my http request having fd as a parameter
The problem is that now I have auto generated forms in my page with angular ng-repeat so I can no more get my file using the input ID, how can I do this now ?
Cheers,
It will be a bit tricky with javascript. this library worked for me. see example below.
<a class="btn btn-sm btn-primary"
type="file"
ngf-select="receiveSlectedFiles($file)">
Select Logo
</a>
whenever you select file, you will get it in receiveSlectedFiles($file) function. when user select valid file it will be in $file if not the $file will be null.
example in controller:
var arrayOfFiles = [];
$scope.receiveSlectedFiles = function (file) {
if (file) {
arrayOfFiles.push(file);
}
};
In service, inject Upload and then use below code to upload image:
this.uploadMultiple = function (files) {
var uri = yourApiUrl;
return Upload.upload({
url: uri,
arrayKey: '',
data: {
files: files
}
})
};
in controller, you just need to pass array of files to this service.
I am developing web application using MVC 4 + VS 2012 + Framework 4.5.
I have three partial views, which are rendering dynamically, on my index page based on user action.
Out of three partial views, one partial view has Upload File functionality with some entry fields like textboxes.
Problem:
When user click on save button (which is present on the partial view itself). I want to save entry field into my database and stored uploaded file on shared folder.
I want to implement this using Ajax (After uploading the file and save data, user should be on the same view).
How can I implement the same? JQuery solution would be fine.
I have tried with #Ajax.BeginForm but after uploading of file, full post back happen.
Here is my small working sample, which uploads multiple files and uploads in a folder called as 'junk'
Client Side....
<html>
<head>
<title>Upload Example</title>
<script src="~/Scripts/jquery-2.1.0.intellisense.js"></script>
<script src="~/Scripts/jquery-2.1.0.js"></script>
<script src="~/Scripts/jquery-2.1.0.min.js"></script>
<script>
$(document).ready(function () {
$("#Upload").click(function () {
var formData = new FormData();
var totalFiles = document.getElementById("FileUpload").files.length;
for (var i = 0; i < totalFiles; i++)
{
var file = document.getElementById("FileUpload").files[i];
formData.append("FileUpload", file);
}
$.ajax({
type: "POST",
url: '/Home/Upload',
data: formData,
dataType: 'json',
contentType: false,
processData: false,
success: function (response) {
alert('succes!!');
},
error: function (error) {
alert("errror");
}
});
});
});
</script>
</head>
<body>
<input type="file" id="FileUpload" multiple />
<input type="button" id="Upload" value="Upload" />
</body>
</html>
Server Side....
public class HomeController : Controller
{
[HttpPost]
public void Upload( )
{
for( int i = 0 ; i < Request.Files.Count ; i++ )
{
var file = Request.Files[i];
var fileName = Path.GetFileName( file.FileName );
var path = Path.Combine( Server.MapPath( "~/Junk/" ) , fileName );
file.SaveAs( path );
}
}
}
This article helped me out: http://www.matlus.com/html5-file-upload-with-progress/
The ActionResult is still ActionResult Upload(HttpPostedFileBase file) {...}
[HttpPost]
public void Upload( )
{
for( int i = 0 ; i < Request.Files.Count ; i++ )
{
var file = Request.Files[i];
var fileName = Path.GetFileName( file.FileName );
var path = Path.Combine( Server.MapPath( "~/Junk/" ) , fileName );
file.SaveAs( path );
}
}
I have a php file which takes a zip file and unpacks it then places it at the desired path on my server.
It works great with a typical form that calls on the php file in the action. I am trying to make this work with AJAX but I have tried every piece of code I can find without any luck.
Is there something here I am missing? Surely this can be done?
Form for uploading the zip file,
<div id="response"></div>
<form enctype="multipart/form-data" method="post" action="">
<label>Choose a zip file to upload: <input type="file" name="zip_file" id="zip_file" /></label>
<br />
<input type="submit" name="submit" value="Upload" onClick="uploadZip()" />
</form>
Current JS - I get no errors, the page actually reloads with my current script..
<script>
function uploadZip() {
formdata = new FormData();
if (formdata) {
$('.main-content').html('<img src="LoaderIcon.gif" />');
$.ajax({
url: "assets/upload-plugin.php",
type: "POST",
data: formdata,
processData: false,
contentType: false,
success: function (res){
document.getElementById("response").innerHTML = res;
}
});
}
}
</script>
php script which handles uploading the zip and unzipping it before placing it on the server.
function rmdir_recursive($dir) {
foreach(scandir($dir) as $file) {
if ('.' === $file || '..' === $file) continue;
if (is_dir("$dir/$file")) rmdir_recursive("$dir/$file");
else unlink("$dir/$file");
}
rmdir($dir);
}
if($_FILES["zip_file"]["name"]) {
$filename = $_FILES["zip_file"]["name"];
$source = $_FILES["zip_file"]["tmp_name"];
$type = $_FILES["zip_file"]["type"];
$name = explode(".", $filename);
$accepted_types = array('application/zip', 'application/x-zip-compressed', 'multipart/x-zip', 'application/x-compressed');
foreach($accepted_types as $mime_type) {
if($mime_type == $type) {
$okay = true;
break;
}
}
$continue = strtolower($name[1]) == 'zip' ? true : false;
if(!$continue) {
$message = "The file you are trying to upload is not a .zip file. Please try again.";
}
/* PHP current path */
$path = '../plugins/'; // absolute path to the directory where zipper.php is in
$filenoext = basename ($filename, '.zip'); // absolute path to the directory where zipper.php is in (lowercase)
$filenoext = basename ($filenoext, '.ZIP'); // absolute path to the directory where zipper.php is in (when uppercase)
$targetdir = $path . $filenoext; // target directory
$targetzip = $path . $filename; // target zip file
/* create directory if not exists', otherwise overwrite */
/* target directory is same as filename without extension */
if (is_dir($targetdir)) rmdir_recursive ( $targetdir);
mkdir($targetdir, 0777);
/* here it is really happening */
if(move_uploaded_file($source, $targetzip)) {
$zip = new ZipArchive();
$x = $zip->open($targetzip); // open the zip file to extract
if ($x === true) {
$zip->extractTo($targetdir); // place in the directory with same name
$zip->close();
unlink($targetzip);
}
$message = "Your .zip file was uploaded and unpacked.";
} else {
$message = "There was a problem with the upload. Please try again.";
}
}
This php function works great as long as I do this with the form action. So I am sure my problem exist in the AJAX function.
Thanks for any help you can provide.
formdata = new FormData();
You've created a FormData object but you never put any data into it.
The easiest approach is to specify the form:
formdata = new FormData(document.forms[0]);
You also need to stop the submit button from actually submitting the form so that the JS can do something.
A cleaner approach would be to:
Stop using intrinsic event attributes
Use the submit handler for the form
Get the form from the event
<input type="submit" name="submit" value="Upload" onClick="uploadZip()" />
Becomes:
<input type="submit" name="submit" value="Upload">
function uploadZip() {
formdata = new FormData();
becomes:
function uploadZip(event) {
var formdata = new FormData(this);
// Rest of function
event.preventDefault();
}
and you add:
jQuery("form").on("submit", uploadZip);