Unset uploaded files in PHP - php

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.

Related

Limit File Uploads To One File - PHP

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.

Fill file input after form submit / on form submit error

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.

Pass uploaded file between PHP scripts

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 :)

PHP file upload

I have a basic upload form:
<form method="post" action="" enctype="multipart/form-data" >
<input type="file" name="logo">
<input type="submit" class="button-primary" value="Upload Image">
</form>
And this is how I upload stuff (these are WordPress functions, but the question is rather php-related, so I'm asking here, not on wp-se):
if ($_FILES) {
foreach ($_FILES as $file => $array) {
$uploaded = insert_attachment($file,$post_id);
$uploaded_src = wp_get_attachment_url($uploaded);
update_option('logo', $uploaded_src);
}
}
Now, there are two issues and I'm not sure how to fix them:
When user uploads a file and clicks "Upload image" the image is being uploaded. But if user refreshes the page the iamge is uploaded once again. and again, and again. I believe the form is sending itself after refreshing, what's the easiest way of repairing that?
As you can see my code updates only one option called "logo", how to get name of upload fields and pass it to foreach loop so I'll be able to put more upload fields on my page? I mean something like: update_option('ThisFormUploadInputID', $uploaded_src);.
Thanks a lot! :)
When user uploads a file and clicks "Upload image" the image is being
uploaded. But if user refreshes the page the iamge is uploaded once
again. and again, and again. I believe the form is sending itself
after refreshing, what's the easiest way of repairing that?
PRG pattern - on successful POST, send Location header and terminate the script. Next request from browser will be GET, and refreshing the page won't resend the form.
As for the second question, print_r($_FILES) might help.
For the second question:
You need more fields to upload more files, or some guiding data? If files so, yes like the answer before - look in var_dump($_FILES). If you need some data inputs so look in $_POST for them.
And about refreshing.
So, yes you can send Location header from php, but you can get an Headers already sent warning. So use JS then <script>document.location="www.yourdomain.com"</script>.

HTML Upload Form will only upload files found in the directory of the PHP file

I have an image uploader that uses the imgur.com API and jQuery's .ajax() function to upload images to their servers. However, if I browse for an image using the <input type="file"/> element of the form, it will only be successful in uploading an image if the image file is found in the same directory as the page.php file that the form is found in (shown below). How can I allow the form to upload images from any directory on my computer?
page.php:
<form action="page.php" method="post">
<input type="file" name="doc" id="doc" /><br/>
<input type="image" src="go.gif" name="submit" id="submit" />
</form>
You've forgotten the enctype="multipart/form-data" attribute on your form tag, for one. Without that, file uploads generally don't work too well.
Beyond that, the server won't really care what directory you're uploading FROM, especially under PHP. The uploaded copy on the server is stored with temporary filename ($_FILES['file']['tmp_name']) that has absolutely nothing to do with the directory/filename on your computer.
Once it's on the server, you'll have to actually move that temporary file somewhere else, as PHP will auto-delete it once the script terminates and you've not handled it yourself. move_uploaded_file() is what's generally used to take of that process.
Perhaps this is the only folder with write-permissions.
I guess it is jquery that is doing the actual posting to http://imgur.com/api/upload as the form is just posting to itself, so my guess is that jquery / javascript can only read files in your web-space and not on your whole hard-drive.

Categories