Retain file info on php/html form

Hi experts,

I have a file upload form in php.   If the form fails validation I want to redisplay it with an appropriate error message.  This is working fine but the file I have selected disappears.

By way of illustration:

 Initial form, with file selected, but fails validation elsewhere:
When redisplayed, the error message show OK, but the file name is no longer there:

When redisplayed, the error message show OK, but the file name is no longer there:
This is my current html for the field in question:

current html
Is there an easy way to do this?

Thanks.
LVL 3
Colin BrazierAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

zephyr_hex (Megan)DeveloperCommented:
It's actually a security feature.

If you just want to display the name of the file the user originally selected, you can store the value in a hidden field, and copy the value over to the file upload input when the user is returned to the form.  This does not save the original file itself (which is the risk), but makes it so the user doesn't have to re-assign the file name to the input.
Dave BaldwinFixer of ProblemsCommented:
Although you many be able to save the file name, it won't save the 'path' in any case.  As zephyr_hex said, it's a security feature that prevents people from 'automatically' selecting files on your computer for upload to their website without your permission.

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Ray PaseurCommented:
No points for this - it's not a solution, just an explanation of the security feature.

Javascript can modify the content of almost every element of the DOM.  It's done by selecting the elements by ID or tagname, then applying CSS styles, JavaScript actions, or HTML and data to the DOM element.  Some of this sort of thing is used to change the meaning and appearance of the browser viewport.  An example might be an auto-complete that detects what you're typing and gives you a selection of likely choices.  Auto-complete scripts may interact with the server, sending AJAX requests with the partial inputs and receiving the suggestions.

Now consider what would happen if your Javascript could populate the contents of the <input type="file" /> tag.  Your Javascript could simply put any file name into the tag and trigger an AJAX request... and... Poof! The server has stolen data from the client browser without the human client's knowledge or consent.

For that reason, the only way that the browser will allow the contents of the <input type="file" /> tag to be populated is by direct human input.  I have not tested this, but I am fairly sure that the client is going to have to select the entire file path again, whether or not your script remembers the base name somewhere.  There should be no way around this gate, and if you find one, I am sure the browser makers will close it off immediately.  Can't comment on what Flash or other "active-X" applications might do with this concept, but it would seem to be a potential risk, and therefore to be minimized.
Determine the Perfect Price for Your IT Services

Do you wonder if your IT business is truly profitable or if you should raise your prices? Learn how to calculate your overhead burden with our free interactive tool and use it to determine the right price for your IT services. Download your free eBook now!

Colin BrazierAuthor Commented:
OK, security feature...I'm happy with that!   and the client will be too.

I really shouldn't award points here?  or just not to you, Ray?

Cheers for the explanations.

Col
zephyr_hex (Megan)DeveloperCommented:
I'm sure you can copy the value to the input and it will work.  I've done it.  Here's how:

Controller code:
ViewBag.CoverLetterPath = application.CoverLetter;
ViewBag.CoverLetterFileName = (coverLetter != null) ? coverLetter.FileName.Trim() : "";

Open in new window


View code:
<label for="CoverLetter">Cover Letter</label>
                                @{var HideCoverName = "";
                                  if (this.ViewBag.CoverLetterFileName != "")
                                  {
                                      <span>@this.ViewBag.CoverLetterFileName</span>
                                      HideCoverName = "HideClass";
                                  }
                                    <input type="file" name="CoverLetter" class="@HideCoverName" id="CoverLetter" accept=".pdf,.doc,.docx,.txt" value="@this.Model.CoverLetter"/>
                                }
                                <input type="hidden" name="CoverLetterPath" value="@this.ViewBag.CoverLetterPath" />

Open in new window

Ray PaseurCommented:
Points?  
Just not to me - There are other deserving responses.  I'm just 'splaining!
Colin BrazierAuthor Commented:
Zephyr_hex, I'm gonna appear thick here but it won't be for the first (or last) time so could you add a couple of lines of explanation for a procedural type guy please?

Is it pseudo-code or embedded javascript?   Do you have classes called Application and Model?

Thanks,
        Col
zephyr_hex (Megan)DeveloperCommented:
The code is following MVC, using C# in the controller and Razor in the view.  To put that in terms of "regular" HTML,  I have two inputs.  One if the file input, and the other is a hidden input.

Here's a watered down version:
<input type="file" name="CoverLetter" id="CoverLetter" />
 <input type="hidden" name="CoverLetterPath"  />

Open in new window


In the Controller code, I'm saving the file to a location (that part isn't showing in the code I posted earlier), and storing the path and file name.  So I'm actually saving the file the first time the form is posted.  I store the value of the file path in the hidden input.  And I store/display the file name in the file input.

When the form is returned to the user, they can see the file name.  If they choose to change the file, I clear out the value in the hidden input, so that I know I shouldn't use the file I previously saved.  If they don't change the file, I know I can use the file I already saved because I have a value in the hidden input.

This logic can be implemented using php & javascript/jquery.  You could tie a javascript event to the file input and clear the value on the hidden input when the user selects a file.  The rest of this can be done using php.
Colin BrazierAuthor Commented:
Sorry I still can't get my head around this.   How can I store the values in hidden fields so they won't be lost when the form reloads?

These are the relevant parts of my code as it is at the moment.
HTML form...I put in the hidden file name input but changed it to test to see what's going on.

<label for="filFileName" id="fileLabel"></label>
<input type="file" id="filFileName" name="filFileName" accept=".pdf,.doc,.docx,.txt,.xls,.xlsx" style="width:25em; color: red;" <?=$disabled;?> onchange="pressed()"/>
                 
<input type="text" name="hdnFilename" id="hdnFilename" value="Col"/>

Open in new window


Jquery...you can see by commenting out code that I have been trying various things.
<script type="text/javascript">
	$( document ).ready(function() {
		fileLabel.innerHTML = "Select file";
	});	
	window.pressed = function(){
		var a = document.getElementById('filFileName');
		if(a.value == "")
		{
			fileLabel.innerHTML = "Pick file";
		}
		else
		{
			var theSplit = a.value.split('\\');
			fileLabel.innerHTML = theSplit[theSplit.length-1];
			document.getElementById("hdnFilename").value = theSplit[theSplit.length-1];
		}
	/*
		$('input[type="file"]').change(function(e){
            var fileName = e.target.files[0].name;
            //alert('The file "' + fileName +  '" has been selected.');
			document.getElementById("hdnFilename").value = fileName;
        });
		document.getElementById("filFileName").value = document.getElementById("hdnFilename").value;*/
    };
</script>

Open in new window

zephyr_hex (Megan)DeveloperCommented:
when the form is posted, the value of the hidden field is in the POST array, just like all of the other form fields.

but... in your code above, i'm only seeing the file name, not the file path, in your html.  the hidden field should store the value of the file path where you've saved the file (when the form was first submitted).  

let's say your html/form is on page1.php and it's submitting to page2.php.  on page2.php, you'll check to see if $_POST['filepath'] exists and has a value.  it will not have a value the first time the user submits their form, so that's your cue to save the file that was submitted, and then add the file path value to $_POST['filepath'].  you already have the file name in $_POST so you don't need to add or change that.

on page1.php, you'll want to check if you have a value in $_POST for the filename and filepath, and use those values for the inputs if the values exist.  so, for value attribute of your inputs, do something like:

value="<? if(isset($_POST['filFileName']) && trim($_POST['filFileName']) != ''){ echo $_POST['filFileName']; } ?>"

Open in new window


do the same for the hidden input:

value="<? if(isset($_POST['hdnFilepath']) && trim($_POST['hdnFilepath']) != ''){ echo $_POST['hdnFilepath']; } ?>"

Open in new window


so that covers the first time the user submits the form and is returned back to it.  let's say they submit the form a second time.  this time, there will be a value in $_POST['filepath'] because you added it the first time and set the value of the hidden input to $_POST['filepath'].  so this tells you that you already saved the file, so you don't need to do anything.  if the user is returned back to the form again, the filepath value will go back to page1.php again.

the only time you'll need to do something different is when the user submits the form the first time with a file, but then changes the file before submitting it again.  to handle that case, you'll want to use jquery.  when the user clicks the file upload input, you'll want to clear out the value in your hidden filepath input.  this will trigger your code on page2.php (when the form is submitted), to save the file and add the value of filepath to $_POST['filepath'].  (side note:  if the user changes the file from file1 to file2, you've already saved file1 and you probably don't want to keep a bunch of unused files.  my method for handling that situation is to save the file with a standard file name (don't use the file name that the user provided).  in my case, the file was always called CoverLetter + record id, so when the user changes from file1 to file2, i'm overwritted my saved copy of file1 with file2).

the jquery would look something like:

$('#filFileName').on('click',function() {
    //clear hidden field so you know a new file was selected
    $('#hdnFilepath').val('');
});

Open in new window


Here is a JSFiddle showing how the jquery works.
Ray PaseurCommented:
only seeing the file name, not the file path
This is a security measure.  Nobody, not the client (human) nor the client (browser) nor JavaScript can populate an input of type="file" except through the normal (human) interaction with the form.  You may be able to do some chicanery with Flash in the browser, but the better path might be to just "ride the horse in the direction he's going."

If you have a lot of form validation, do it in JavaScript before the file is submitted.  Then you can get all of that complicated stuff out of the way and your client will not have to repeatedly find / select / upload the file.  Maybe consider using a two-page form - the first page submits all of the stuff that needs validation, the second page selects the file for upload.
zephyr_hex (Megan)DeveloperCommented:
Ray-  Did you read the method I'm using?  The file path I'm talking about is the one generated when *I* save the file that's posted when the form is submitted the first time, not the file path from the client's machine.

The basic premise of my approach is to save the posted file the first time the form is posted, and any time the file is changed, and holding the value of the file path where the file is saved on the server in a hidden field.
The file name that the user sees is just for show... so they know they don't need to re-select the file.

Also, as a side note, client side validation is fine and dandy, but it should never replace server side validation.
Dave BaldwinFixer of ProblemsCommented:
It appears to me that you are still trying to fill an "<input type='file' " with a method that is not allowed.
Colin BrazierAuthor Commented:
I forgot the important point that I do actually save the file to a temp location on first submittal and when the form passes validation I move it to the right place, but if the user cancels out there's some housekeeping to do.  Maybe this isn't best practice. I suspect not.

Anyhow, I think there's a time difference thing going on here, I'll come back to this Monday.

Again, thanks for your help and have a good weekend all.
zephyr_hex (Megan)DeveloperCommented:
In order for the method I've suggested to work, you'd need to save the file on the first submit.
Colin BrazierAuthor Commented:
In order for the method I've suggested to work, you'd need to save the file on the first submit.
 Agreed, and as it can only accept "harmless" file types, not an issue.

value="<? if(isset($_POST['filFileName']) && trim($_POST['filFileName']) != ''){ echo $_POST['filFileName']; } ?>"

Open in new window


But there's no such thing as $_POST['filFileName'] since the value of filFileName would be in the $_FILES array since it is type="file"?
zephyr_hex (Megan)DeveloperCommented:
@colinspurs.  Entirely possible.  I was trying to translate my asp.net MVC C# code to the PHP equivalent.
Colin BrazierAuthor Commented:
So it can't be done? (not a problem).
zephyr_hex (Megan)DeveloperCommented:
It can be done, you'd just use $_FILE array instead of $_POST to get the filename.
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
HTML

From novice to tech pro — start learning today.