I've got a page where users can upload a HTML file for use as a theme. The HTML file has some checks performed on it before some options are displayed to the user. The user fills out the form in relation to the HTML file, and submits the form. However, due to the file in the temp folder being destroyed after the script has ended, I do not know how to get the HTML file once the form has been filled out and re-submitted short of making the user re-upload the file, which relies on them uploading the same file, and also makes them upload something twice, which is counter-intuitive and could be an issue with large files.
Here is the code (cut down to make it easier to read/understand). The form submits to itself, so the same PHP file is used for both "steps".
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
// Form has been submitted
$files = $_FILES['file'];
<form method="POST">
/* Options about the uploaded HTML file are generated using PHP and displayed here */
<input type="submit">
</form>
} else {
?>
<form method="POST" enctype="multipart/form-data">
<label for="file">Theme File(s)</label>
<input type="file" name="file[]" multiple="multiple">
<input type="submit">
</form>
<?php
}
I tried using <input type="file" name="file[]" multiple="multiple" value="<?php echo $files; ?> >, but this did not work, and would also require the user to re-upload the files, which could be any issue if the files get too big.
I was thinking there might be a way to pass the file internally, and have a check whether a file has been uploaded or passed to the script instead of <input type="file" name="file[]" multiple="multiple">, but I could not find a way to do that.
You need to create your own temporary file, and pass the name between your two scripts.
So for example, in your "first script" (i.e. when the file has first been uploaded) you would:
$uniqName = uniqid('upload_', TRUE);
$tmpFile = "/tmp/$uniqName.html";
move_uploaded_file($_FILES['file']['tmp_name'][0], $tmpFile);
And then when you generate the form from the result of this upload, you would add
<input type="hidden" name="uniqName" value="<?php echo $uniqName; ?>" />
...so that when you get to your "second script" (after the questionnaire form is submitted) you can access the file through:
$tmpFile = "/tmp/".basename($_REQUEST['uniqName']).".html";
Of course, this is subject to the possibility of people failing to submit to second form so you end up with "orphaned files" littering your temporary directory, so you will need to implement some form of check that deletes files after thet have been inactive for a certain amount of time - you can base this on the last modified time of the files.
EDIT
Here is an example of how you can randomly run a job to keep the /tmp dir tidy without a cron job:
$probabilityDivisor = 10; // 1/10 chance of running
$uploadTTLSecs = 1800; // 30 minutes
if (mt_rand(1, $probabilityDivisor) / $probabilityDivisor == 1) {
foreach (glob('/tmp/upload_*') as $file) {
// Iterate files matching the uniqids you generate
if (time() - filemtime($file) >= $uploadTTLSecs) {
// If file is older than $uploadTTLSecs, delete it
unlink($file);
}
}
}
This operates in a similar way to the PHP session garbage collectors. Because of the simplicity of the operations, this should not adversely affect the user experience in any meaningful way.
You could try the following:
Use move_uploaded_file() and move the file from the temp location to a permanent location on the server. Be sure to give the file your moving a unique name like 'file-' . time() . '.jpg' or something.
Soon after uploading, register a Session variable and put the file name in it.
After everything is over you could delete the file using unset()
Now the file is located safely on your server and you also have access to it via the session.
Hope this helps :)
Related
I have a file input element that is used to upload a profile image to a user profile page. By default browsers only allow this element to upload one file unless you add the multiple attribute.
I'd like to set a back up in the PHP though just in case someone decides to add the 'multiple' attribute in the HTML.
I thought assigning the $_POST superglobal to a variable and having an if statement saying if this value is greater than 1 would be prevent this, but it doesn't?
What is the best way to approach this? I've tried various things such as the count() array method but can't seem to find a solution to what seems like a very simple problem?
if(isset($_POST['submit-profile-image'])) {
$profileImage = $_POST['submit-profile-image'];
if (isset($profileImage)) {
if ($profileImage > 1) {
$error[] = "You cannot upload more than one profile image";
}
}
// ALL OTHER CODE
}
I've also tried using the $_FILES superglobal and counting the instances of the $_FILES['profile-image']. This encounters a different problem in that it blocks more than one file upload BUT also blocks single file uploads (and I don't understand why)?
if(isset($_FILES['profile-image'])){
if(count($_FILES['profile-image']) > 1){
$error[] = "You cannot upload more than one profile image";
}
}
You can't prevent a user sending multiple files. All you can do is defend against the possibility, and fail gracefully.
Let's assume that your HTML includes this <form>
<form method='post' enctype="multipart/form_data">
<input type='file' name='uploadFile'>
<input type='submit' name='submit'>
</form>
When the user selects a file and clicks submit the browser packs up the file and sends it, PHP unpacks the file to the server disk, and then presents the file details to your program in the $_FILES['uploadFile'] array.
If we assume that the user edits your HTML and adds multiple then the browser will pack up the files and send them. PHP will unpack the first file and add its detail to $_FILES['uploadFile'] as before. It will then unpack the second file and place its details in $_FILES['uploadFile'], overwriting the first file. Your program sees only one file, knows nothing of any other file, and carries on.
To get two files your user will have to change the name of the file input to use array syntax, so lets suppose he changes the line to
<input type='file' name='uploadFile[]' multiple>
Now PHP unpacks the file details into a set of arrays. Instead of having $_FILES['uploadFile']['name'] containing a string with one filename, it becomes an array of strings.
It is likely that your code, expecting a string, will choke on an array and fail in some unexpected way. You can check for this condition with
if (is_array($_FILES['uploadFile']['name'])) {throw new Exception("Too many files");}
So, our user, determined to force this extra file on you now adds a second <input> to the form:
<form method='post' enctype="multipart/form_data">
<input type='file' name='uploadFile'>
<input type='file' name='uploadFile'>
<input type='submit' name='submit'>
</form>
The second file overwrites the first as it has the same name. Your program is none the wiser and carries on with just one file. So the user changes the name on the second input:
<form method='post' enctype="multipart/form_data">
<input type='file' name='uploadFile'>
<input type='file' name='uploadFileB'>
<input type='submit' name='submit'>
</form>
You could check for this by looking at count($_FILES), but your program isn't looking for a second input, so it will ignore it anyway and carry on handling just the first file. If the user also changes the first name your program won't see any files, and if he reverses the names, your program will see just the second file and ignore the first.
Alternatively, set the PHP configuration value in PHP.INI:
max_file_uploads = 1;
If you do this, PHP will ignore the second and subsequent files. Your code will still have to deal with the naming issues. Setting this with ini_set() doesn't seem to have any effect.
I have a form with text inputs and file inputs; the text fields are being validated. Is there a way to have the form remember which files the user has already selected if they hit submit but need to go back because one of the text fields didn't validate?
You can't "pre-fill" the contents of a file upload field for security reasons. Also, that would mean the file would get re-uploaded every time the form is submitted, which would not be good.
Instead, do this:
Create a file upload field with name file_upload.
On the server-side, process the upload in any case, even if the rest of the form validation fails.
If the form validation failed, but the file was uploaded, insert a hidden input into the form with name file containing the name of the just uploaded file.
Display a user-visible indication that the file is okay. If it's an image, display a thumbnail version of it. If it's any other file, display its filename and/or icon.
If the user chooses to upload a different file in the file_upload field, process the upload and store the new value in file.
Pseudocode:
<?php
$file = null;
if (!empty($_POST['file'])) {
$file = $_POST['file'];
}
if (!empty($_FILES['file_upload'])) {
// process upload, save file somewhere
$file = $nameOfSavedFile;
}
// validate form
?>
<input type="file" name="file_upload" />
<input type="hidden" name="file" value="<?php echo $file; ?>" />
<?php
if (!empty($file)) {
echo "File: $file";
}
?>
Important note
This mechanism can allow any user to claim other user's files as their own, by including a file name that they guessed exists on your server. You will want to ensure that uploaded files are clearly associated with a specific user to avoid this issue.
files input fields are read-only you can't set an initial value for them
You can upload the Files anyway and display the filenames instead of the file select box. To remember the fields, you can use a $_SESSION variable.
I´ve a multipart form with a mixture of default inputs (text, select etc.) and a file upload (<input type="file">).
I´m using a combination of form validation class and upload library of Codeigniter for form submission. That works great.
I´ve only one problem for what I haven´t found a solution yet: If the user selects an image but misses to fill another required field (like "name"), then the form validation class blocks the request and shows an error message to the customer.
But now I´ve the problem, that the image was already submitted successfully and I don´t want to let the user add the file again. So I want to pre-fill the file input with this data.
I´ve tried different things like:
<input type="file" name="image" value="<?php echo set_value('image','');?>" />
and also spent time on finding a solution on the web but without success.
On the server side, you do not get any information about where the file is located on the client's computer, so in the scenario of a user uploading an image successfully but the user hasn't filled out the rest of the fields properly, you have to simply omit the input type="file" field entirely but keep a store of where the file is located on your server. There's a few ways to go about this, but it all involves taking the absolute location of the uploaded file and:
Inserting it back as a hidden value using <input type="hidden" name="uploadedFile" value="<?php echo $absPath; ?>" /> then checking for the existence of $_POST['uploadedFile'] and utilizing it appropriately. But this isn't a solid idea as you're now exposing server paths to the end-user (opens yourself up to malicious attack.)
Starting a session and saving the absolute path in the $_SESSION variable while presenting the user with a simple token in their re-attempt form.
I'd stick with method 2, so assuming you've done all the work to validate the form and upload the file and your file is located in $absFilePath, you could do the following:
session_start(); // This needs to be at the very top of you PHP file
// ...
$formToken = md5(time());
$_SESSION['uploadedFile'][$formToken] = $absFilePath;
Then render the token as a hidden variable using:
if (!empty($_SESSION['uploadedFile'][$formToken]))
echo '<input type="hidden" name="formToken" value="'.$formToken.'" />';
and hide the file upload portion using
if (empty($_SESSION['uploadedFile'][$formToken]))
echo // <input type="file" output here...
finally inside of your form submission code check for the existence of a formToken value before attempting to load $_FILES['image'] using isset($_POST['formToken']), and handle it using:
$absFilePath = $_SESSION['uploadedFile'][$_POST['formToken']];
Bam! Now you have your absolute file path as if the file had been uploaded just like before.
Since you haven't given enough code, I can only given you enough instruction to get you started, but this should be more than enough.
I have an assignment for school, and I'm not sure how the teacher wants us to accomplish a task.
We need to get an uploaded file as a temp file only (index.php)
Output size of file (upload.php)
User can confirm save of file or not (upload.php)
So, I have the majority down, but my problem lies with creating the temp file into a permanent file.
index.php
<html>
<form action="http://mysite.org/~me/upload.php" method="POST" enctype="multipart/form-data">
<input type="file" name="file"><br />
<input type="submit" value="Now upload it!">
</form>
</html>
upload.php
<?php
if (($_FILES["file"]["type"] == "application/vnd.ms-excel"))
{
if ($_FILES["file"]["error"] > 0)
{
echo "Return Code: " . $_FILES["file"]["error"] . "<br />";
}
else
{
echo "Size: " . ($_FILES["file"]["size"] / 1024) . " Kb<br />";
}
}
?>
<form action="" method="POST">
<input type="submit" value"YES please save">
<form>
<?php
if (isset($_POST['submit']))
{
//Code for saving file
echo 'File saved!';
}
?>
Is it possible to go about it this way? My last echo statement does not work, so I'm doubtful the file save would be as well.
Hopefully the following comments can help you with the part you are stuck on.
In case you hadn't realized it already, any files uploaded with PHP are deleted once the PHP request that handled the uploaded file terminates. This means, if you don't do anything with the temp file from the upload, it will be deleted when the PHP script terminates.
One function of interest to you will be move_uploaded_file() which will move the temporary file from the upload to a permanent location of your choice.
Since the file will be uploaded and then you have to display the size and ask the user to confirm the upload, you will have to move the temp file to a permanent temporary location where it is kept when the user hasn't confirmed they want to keep the upload.
I'm not sure if you have been introduced to sessions yet, but if not, you will probably need some hidden form element that will keep track of what file they uploaded, otherwise you can keep this info in the session.
Then when the person submits the form saying they want to keep the file, you can move it again to a permanent location, or if they say no, then delete the file. The problem is, if they never say yes or no, then the file remains on the system.
Hope that helps.
Yep, this should work. Your if statements will catch the form submission and then echo your string there. A few little errors in your markup:
<input type="submit" value"YES please save">
Should be
<input type="submit" value="YES please save" name="submit">
Your final if statement in PHP is looking for a post variable named 'submit' but your <input type="submit"> tag has no name.
The file is saved to a temporary location when the upload completes. You can access this temporary file with $_FILES['file]['tmp_name'] BUT the file will be removed at the end of the request if you do nothing about it. This means that when the user clicks YES please save button, the file will not be available any more.
This means that you have to save the file to a disk in the first place, when you first call the upload.php file. There is no way to keep the file "in memory" while the user decides whether or not to save the file permanently.
I have a Form that I am using to receive an uploaded .csv file, parse it and insert the data into my MySQL db on an Apache server. The page first checks to see if there is an uploaded file. If there is, it processes the data, if not the form (below) is displayed.
<form enctype="multipart/form-data" action="uploadfaculty.php" method="POST" id="UploadForm">
Choose a file to upload: <input name="uploadedfile" type="file" /><br />
<input type="submit" value="Upload File" />
</form>
My problem is that currently the user can simply F5 the browser over and over again and because the file is still on the server and in the $_FILES array, it processes it every time.
I've tried:
unlink($_FILES['uploadedfile']['tmp_name']),
unlink($_FILES['uploadedfile']),
unset($_FILES['uploadedfile']['tmp_name']), and
unset($_FILES['uploadedfile'])`
I've even reset the form via Javascript (which I knew would not work, but did it just to eliminate all doubt). All to no avail. I'm sure it's something simple I'm missing...it almost always is. Any thoughts?
It's not unsetting because the post action is stored on the browser's end and being re-uploaded (in a small amount of time as it's only a csv) when they hit F5. Which is essentially the same as them using the form to upload another csv.
You can do this:
if (isset($_POST['csv'])){
$DataProcessed = DataProcessingFunction();
}
if (isset($DataProcessed) && $DataProcessed){
header("Location: /path/to/form/page.php");
exit();
}
This will clear the post data sent in the earlier request. Refreshing will not resubmit the form.
You can header redirect them to that upload page after processing to prevent the post data from continually going in via a refresh. But the temporary file should be cleared once the processing is done. PHP does not keep the file unless you use the move_uploaded_file function.