Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations IamaSherpa on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Multiple File Upload Getting File Names 1

Status
Not open for further replies.

PCHomepage

Programmer
Feb 24, 2009
609
US
I have a simple form for uploading multiple files to the file system:

Code:
<form method="POST" enctype="multipart/form-data" action="fileupload.php">
<p>CSV / XML / XMLX files only:<br>
<input type="file" name="userfile[]" /><br>
<input type="file" name="userfile[]" /><br>
<input type="file" name="userfile[]" /><br>
<input type="submit" value="Upload All" />
</form>

... and a script for processing the upload:

Code:
foreach ($_FILES["userfile"]["error"] as $key => $error) {
    if ($error == UPLOAD_ERR_OK) {
        $tmp_name = $_FILES["userfile"]["tmp_name"][$key];
        $name = $_FILES["userfile"]["name"][$key];
        move_uploaded_file($tmp_name, "$FileUploadPath/$name");
    }
}

It all works as needed so far but I also want to grab the file names and insert them into the database. I've tried various things including this but all I seem to be able to get is the last file name so I'm sure I just must something obvious that needs another pair of eyes to spot or should I be doing it as part of the code above?

Code:
//check if a file has been submitted, insert name into database
if(isset($_FILES['userfile']['tmp_name'])) { 
	// loop through the array of files // 
	for($i=0; $i < count($_FILES['userfile']['tmp_name']);$i++) { 
		if (is_valid_file_size($_FILES['userfile']['size'][$i]) 
		    && is_uploaded_file($_FILES['userfile']['tmp_name'][$i]) )  {     
 
 			$Filename .= $_FILES[$i];
                       // mysqli_query("INSERT INTO file_uploads(FileName) VALUES('$FileName')");  
  		echo $FileName;
		} else { 
			$warning = "Error uploading file(s).";
		} 
	} 
}

Any ideas? Thanks in advance.
 
Code:
$Filename .= /*$_FILES[$i]*/ $_FILES['userfile']['name'][$i];
 
Thank you! That makes more sense but it seems to be giving a server error now so I'll have to continue with it next week. I did see that the same line you suggested is in my file upload code (which has been modified to automatically create a subfolder) and I am able to echo more than one file name to the screen but the names are as one so it would be impossible to parse out the individual names that way.

Code:
foreach ($_FILES["userfile"]["error"] as $key => $error) {
	if ($error == UPLOAD_ERR_OK) {
		$tmp_name = $_FILES["userfile"]["tmp_name"][$key];
		$name = $_FILES["userfile"]["name"][$key];
		$name = trim(str_replace(" ", "_", $name));
		$SubFolder = explode("_", $name);
		$oldumask = umask(0);
		mkdir("$FileUploadPath/$SubFolder[0]", 01777);
		umask($oldumask);
		$FileUploadFolder = "$FileUploadPath/$SubFolder[0]";
		move_uploaded_file($tmp_name, "$FileUploadFolder/$name");
		echo $name; 
	}
}

Since there will be a finite number of files uploaded at a time, I thought perhaps I could get the individual names with $_FILES["userfile"]["name"][0] etc. but I guess not as it still brings up multiple names as one string when I echo it to the screen.
 
I just realized that it is not showing both file names (if two are uploaded). It's showing the name for whichever $key value is used but it's showing it twice back to back.
 
yes. because you are concatenating. remove the dot before the =.
 
That's what I thought too but there is no dot in the latest version that I posted above but perhaps I need to do it outside the loop.
 
Just tested your code:

Code:
foreach ($_FILES["userfile"]["error"] as $key => $error) {
	if ($error == UPLOAD_ERR_OK) {
		$tmp_name = $_FILES["userfile"]["tmp_name"][$key];
		$name = $_FILES["userfile"]["name"][$key];
		$name = trim(str_replace(" ", "_", $name));
		$SubFolder = explode("_", $name);
		$oldumask = umask(0);
		mkdir("$FileUploadPath/$SubFolder[0]", 01777);
		umask($oldumask);
		$FileUploadFolder = "$FileUploadPath/$SubFolder[0]";
		move_uploaded_file($tmp_name, "$FileUploadFolder/$name");
		echo $name; 
	}
}

And I get all filenames correctly from the echo. Yes they are all together, because you are echoing them out like that. If you add say a line break they'll be separate.

With that said, this:

Code:
$Filename .= $_FILES[$i];
Is wrong, as $_FILES is an array but has no numbered indexes, only the name of your file input elements, which in your case would be $_FILES['userfile'].

The server error you are getting is probably an undefined index, because there would be no $_FILES[1] or $_FILES[2];.

Just a suggestion, but I always try to output the arrays I'm working with to understand their format before trying to process them. I have my own dump function that outputs in a nice table format, but you cna use a regular <pre> tag and print_r to get the array structure easily.

In your case the $_FILES array should look like this:

Code:
Array
(
    [userfile] => Array
        (
            [name] => Array
                (
                    [0] => file1.txt
                    [1] => file2.txt
                    [2] => file3.txt
                )

            [type] => Array
                (
                    [0] => text/plain
                    [1] => text/plain
                    [2] => text/plain
                )

            [tmp_name] => Array
                (
                    [0] => C:\wamp\tmp\phpA95F.tmp
                    [1] => C:\wamp\tmp\phpA960.tmp
                    [2] => C:\wamp\tmp\phpA961.tmp
                )

            [error] => Array
                (
                    [0] => 0
                    [1] => 0
                    [2] => 0
                )

            [size] => Array
                (
                    [0] => 0
                    [1] => 0
                    [2] => 0
                )

        )

)

That means $_FILE is an array with a single index, of userfile that is also an array that has several indexes which are also arrays.


----------------------------------
Phil AKA Vacunita
----------------------------------
Ignorance is not necessarily Bliss, case in point:
Unknown has caused an Unknown Error on Unknown and must be shutdown to prevent damage to Unknown.

Web & Tech
 
Thank you for that! I did output the array just as you described but I'm afraid that I am quite rusty on programming so have forgotten how to use them!

Putting a break would be fine if I wanted to only display theme but I need them somehow as separate variables so that I can process the name and insert it into the database.
 
Arrays are easy, they are simply lists of items. The lists can be numbered, or thy can have string indexes, and they can contain other lists.

Code:
Putting a break would be fine if I wanted to only display theme but I need them somehow as separate variables so that I can process the name and insert it into the database.
Turn them into an array, or even just a list, if you need to use them later on outside the foreach loop.

Code:
$nameList[] = trim(str_replace(" ", "_", $name));

Once the foreach loop is done, you'll have the filenames in an array you can use. Alternatively you can go through the $_FILES array again if you need to. Or even just do the DB processing inside the foreach loop.






----------------------------------
Phil AKA Vacunita
----------------------------------
Ignorance is not necessarily Bliss, case in point:
Unknown has caused an Unknown Error on Unknown and must be shutdown to prevent damage to Unknown.

Web & Tech
 
I Posted this earlier but it seems to have disappeared so I hope this isn't a duplicate! I have it working although the code is not especially elegant or streamlined but at least it works. It still needs a way of checking that a given file hasn't already been uploaded but if it has, to offer to upload a fresh copy without reinserting a new copy of this filename data. That part should be easy.

The hard part is that the CSV content itself also needs to be inserted/updated and it has an odd three-part structure that needs to be parsed and inserted/updated into three different tables but I'll cross that bridge when I come to it.

Thank you for the help!

Code:
<?php
include_once $_SERVER['DOCUMENT_ROOT']."/test/configuration/mysql_conf.php";
include_once $_SERVER['DOCUMENT_ROOT']."/test/functions/explodeforce.php";

$FileUploadPath = $_SERVER['DOCUMENT_ROOT']."/test/uploads";

foreach ($_FILES["userfile"]["error"] as $key => $error) {
	if ($error == UPLOAD_ERR_OK) {
		$tmp_name = $_FILES["userfile"]["tmp_name"][$key];
		$name = $_FILES["userfile"]["name"][$key];
		$name = trim(str_replace(" ", "_", $name));
		$SubFolder = explode("_", $name);
		$oldumask = umask(0);
		mkdir("$FileUploadPath/$SubFolder[0]", 01777);
		umask($oldumask);
		$FileUploadFolder = "$FileUploadPath/$SubFolder[0]";
		move_uploaded_file($tmp_name, "$FileUploadFolder/$name");
		$nameList[] = $name; 
	}
} 

$FileName1 = $nameList[0];
$FileName2 = $nameList[1];
$FileName3 = $nameList[2];

// explodeForce is a custom function that forces pieces to a specific number
// basename() strips off file name .csv extension
$Pieces1 = explodeForce("_", basename($FileName1, ".csv"), 5);
$Pieces2 = explodeForce("_", basename($FileName2, ".csv"), 5);
$Pieces3 = explodeForce("_", basename($FileName3, ".csv"), 5);

$Query1 = $mysqli->prepare("INSERT INTO file_uploads(FileName, Value1, Value2, Value3, Value4, Value5) 
	VALUES('$FileName1', '$Pieces1[0]', '$Pieces1[1]', '$Pieces1[2]', '$Pieces1[3]', '$Pieces1[4]')");

$Query2 = $mysqli->prepare("INSERT INTO file_uploads(FileName, Value1, Value2, Value3, Value4, Value5) 
	VALUES('$FileName2', '$Pieces2[0]', '$Pieces2[1]', '$Pieces2[2]', '$Pieces2[3]', '$Pieces2[4]')");


$Query3 = $mysqli->prepare("INSERT INTO file_uploads(FileName, Value1, Value2, Value3, Value4, Value5) 
	VALUES('$FileName3', '$Pieces3[0]', '$Pieces3[1]', '$Pieces3[2]', '$Pieces3[3]', '$Pieces3[4]')");

if ($FileName1) { $Query1 ->execute(); }
if ($FileName2) { $Query2 ->execute(); }
if ($FileName3) {	$Query3 ->execute(); }

echo "These file name have been inserted into the database and the files uploaded for processing!<p>";

echo $FileName1."<br>";
echo $FileName2."<br>";
echo $FileName3."<br>";
?>
 
This was working reasonably well on my system at home but now in the office the same code crashes with a server error. The problem seems to be in the portion highlighed in red below:

Code:
foreach ($_FILES["userfile"]["error"] as $key => $error) {
	if ($error == UPLOAD_ERR_OK) {
		$tmp_name = $_FILES["userfile"]["tmp_name"][$key];
		$name = $_FILES["userfile"]["name"][$key];
		$name = trim(str_replace(" ", "_", $name));
		$SubFolder = explode("_", $name);
		$oldumask = umask(0);
		mkdir("$FileUploadPath/$SubFolder[0]", 01777);
		umask($oldumask);
		$FileUploadFolder = "$FileUploadPath/$SubFolder[0]";
		move_uploaded_file($tmp_name, "$FileUploadFolder/$name");
		$nameList[] = $name; 
	}
}

[COLOR=red]$FileName1 = $nameList[0];
$FileName2 = $nameList[1];
$FileName3 = $nameList[2];[/color]

If I remark it and all below it, it does not crash and if I echo $name or $nameList[0];
to the screen, it gives the proper values so I don't see how simply assigning a variable name to it can make it crash like it's doing! Any ideas?
 
Post the error you are getting.



----------------------------------
Phil AKA Vacunita
----------------------------------
Ignorance is not necessarily Bliss, case in point:
Unknown has caused an Unknown Error on Unknown and must be shutdown to prevent damage to Unknown.

Web & Tech
 
I was just getting a generic server error but it's gone now. I think there were incorrect line endings from the FTP from work to home and retyping those few lines sorted it out.

Anyway, it was all working just fine but it had a lot of repetitive code in order to handle multiple files so I moved it into a function where I hope to make it process them all without the repetition. However, I'm not sure how to pass multiple file names into the function. $Files is coming from the processing file and loaded into the function with:

Code:
$Files = $_FILES['Userfile'];
uploadProbeCSV($Files, $mysqli);

The function starts with this (the rest is mostly just SQL updates and inserts) but it is not giving me any useful $Filename values. In fact, it seems to bypass this section unless I do NOT select a file to upload so clearly I am misunderstanding if ($error == UPLOAD_ERR_OK) bit and have it backwards!

It seems to be producing and array but I can see it only when I print_r the $Files variable at the end of the function. Any thoughts?

Code:
function uploadCSV($Files, $mysqli) {
	// Function for uploading and logging Probe Data CSV files
	// Initialize values
	// Get the current time date as Unix timestamp
	$CurrentTime = mktime();
	$Output = '';

	foreach ($Files["error"] as $key => $error) {
		if ($error == UPLOAD_ERR_OK) {
			$tmp_name = $Files["tmp_name"][$key];
			$Filename = $Files["name"][$key];
			$size = ($Files["size"][$key]);
			$Filename = trim(str_replace(" ", "_", $Filename));
			$Output .= $Files["name"][$key];
			$Subfolder = explode("_", $Filename);
			$oldumask = umask(0);
			mkdir("$UploadPath/$Subfolder", 01777);
			umask($oldumask);
			$Folder = "$UploadPath/$Subfolder";
			move_uploaded_file($tmp_name, "$Folder/$Filename");
 
$Files = $_FILES['Userfile'];
uploadProbeCSV($Files, $mysqli);

That's fine. Though you could have simply used the $_FILES superglobal.

Anyway, from there on, the foreach loop should be working fine. Except for a couple of issues:

[ol]
[li]You never define $UploadPath in your function.
Code:
                        $Subfolder = explode("_", $Filename);
			$oldumask = umask(0);
			mkdir("[b][red]$UploadPath[/b][/red]/$Subfolder", 01777);
That alone, should be issuing a Warning.
[/li]
[li] As you have exploded the $Filename variable, $Subfolder is now an array, so you would need to choose a key to use from it.
Code:
                        $Subfolder = explode("_", $Filename);
			$oldumask = umask(0);
			mkdir("$UploadPath/[green][b]$Subfolder[/b][/green][red][b][x][/b][/red]", 01777);
[/li]

[/ol]

Together they would be producing invalid paths, and throwing additional errors.



----------------------------------
Phil AKA Vacunita
----------------------------------
Ignorance is not necessarily Bliss, case in point:
Unknown has caused an Unknown Error on Unknown and must be shutdown to prevent damage to Unknown.

Web & Tech
 
Thanks for that. I'll continue to look through to see why it seems to be giving better results (at least I see my on-screen messages) when I do not even select a file but I see nothing when I do. I agree with your comments but it does not appear to even be reaching these places in the function to give an error or not, and seems to be going directly to a message at the end that is an "else" after all this to say that nothing was uploaded.

On the $UploadPath and $Subfolder values, it was all working as regular PHP before it was converted to a function and the variable $Subfolder simply contains the first segment of the file name from the prior explode(). Sometimes they all get uploaded to the same subfolder but if it's a new file with a different beginning sequence, then it needs its own folder. If the folder does not exist for a particular file, then the mkdir() is creating it and it's inside the looping so that it can create it on-the-fly if needed.

$UploadPath is not defined here but it is in the site's configuration file which is included into the function which, for simplicity I didn't show.
 
nd seems to be going directly to a message at the end that is an "else" after all this to say that nothing was uploaded.

Assuming the else belongs to this if statement:
Code:
if ($error == UPLOAD_ERR_OK) {

That would seem to be the correct behavior if there was a problem uploading one of the files.
Are you sure they are getting uploaded correctly?

Try dumping the $Files at the beginning of the function, see if there are any errors in the error array.

As far as $Subfolder goes, exploding returns an array, so its now an array. Regardless of how many indexes it has it still has to be treated as an array.
it was all working as regular PHP before it was converted to a function and the variable $Subfolder simply contains the first segment of the file name from the prior explode()

It probably wasn't correctly, and it contains all segments after the explode. If your filename looked like:
part1_part2, it would have two indexes, one for each part.

Code:
Array
(
    [0] => part1
    [1] => part2
}









----------------------------------
Phil AKA Vacunita
----------------------------------
Ignorance is not necessarily Bliss, case in point:
Unknown has caused an Unknown Error on Unknown and must be shutdown to prevent damage to Unknown.

Web & Tech
 
Thank you again. The main problem it the last issue seemed to be related to the physical file itself. Although it looked okay, it was FTP incorrectly. I created a new file and reentered the code (with some slight changes) and it works well now. I have not yet re-added the mysqli queries but will do so later.
 
Glad you worked it out.

----------------------------------
Phil AKA Vacunita
----------------------------------
Ignorance is not necessarily Bliss, case in point:
Unknown has caused an Unknown Error on Unknown and must be shutdown to prevent damage to Unknown.

Web & Tech
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top