This question already has an answer here:
Adding multiple files with PDO
(1 answer)
Closed 7 years ago.
I have made a script to upload multiple files using a form:
<form action="upload_image.php" id="form_img" method="POST" enctype="multipart/form-data">
<div align="center">
<div class="fileUpload btn btn-primary">
<span>Carica immagini nella galleria</span>
<input type="file" name="immagini[]" multiple="multiple" id="file_img" class="upload"/>
<script>
document.getElementById("file_img").onchange = function() {
document.getElementById("form_img").submit();
};
</script>
</div>
</div>
</form>
The javascript code is supposed to submit the form when user have chosen a file and here is the php I am using to process the upload:
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
session_start();
$where = dirname(__FILE__);
include($where . "/config/db.php");
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
foreach ($_FILES as $file) {
$nome_file_temporaneo = $file["tmp_name"];
$nome_file_vero = $file["name"];
$tipo_file = $file["type"];
$not_profilo = '1';
for($i=0;$i<sizeof($tipo_file);$i++) {
$dati_file = file_get_contents($nome_file_temporaneo[$i]);
$query = "INSERT INTO ".$_SESSION['id']." (immagine,type,profilo) values (?,?,?)";
$stmt = $dbh->prepare($query);
$stmt->bindParam(1, $dati_file, PDO::PARAM_LOB);
$stmt->bindParam(2, $tipo_file[$i],PDO::PARAM_STR);
$stmt->bindParam(3, $not_profilo, PDO::PARAM_STR);
$stmt->execute();
}
}
header("location: profile_set.php");
?>
This gives me an error:
Fatal error: in C:\xampp\htdocs\tp\upload_image.php on line 24
Line 24 is the line that contains: $stmt->execute()
Any help would be appreciated.
Try binding using an array inserted into the ->execute(array()). If you want to make sure that values are what they should be, just do some validation in the foreach() loop. One last thing, you say your form does multiple uploading but you have only one input and you have it upload as soon as the input changes, so that is a tad confusing:
// I am just saving your connection to a function just to clean it up a bit
function connection()
{
include(__DIR__."/config/db.php");
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $dbh;
}
// I like to reogranize my $_FILES array so each file is in it's own array
function organize($array = false)
{
if(!is_array($array) || empty($array))
return $array;
foreach($array['name'] as $key => $value) {
if($array['error'][$key] != 0) {
$files[$key] = false;
continue;
}
$files[$key] = array(
"name" => $array['name'][$key],
"tmp_name" => $array['tmp_name'][$key],
"type" => $array['type'][$key],
"error" => $array['error'][$key],
"size" => $array['size'][$key]
);
}
return $files;
}
// This will return an array of bind values and statement values
function CompileUpload($use_name = 'immagini')
{
// If empty, do nothing
if(empty($_FILES[$use_name]))
return false;
//Reorganize array
$FILES = organize($_FILES[$use_name]);
$return = false;
foreach ($FILES as $i => $file) {
if($file["error"] !== 0)
continue;
// I would suggest just saving the name and location of
// the file(s) instead of saving them to the database.
$temp = $file["tmp_name"];
$name = $file["name"];
$type = $file["type"];
$data = file_get_contents($temp);
// Create a bind array
$bind[":".$i."name"] = $name;
$bind[":".$i."type"] = $type;
$bind[":".$i."data"] = $data;
// Create the append values for the sql statement
$bCols[$i][] = ":".$i."name";
$bCols[$i][] = ":".$i."type";
$bCols[$i][] = ":".$i."data";
// Implode and save to a master row array
$iCols[] = "(".implode(",",$bCols[$i]).")";
}
// If there is no bind array (errors in file array)
// just return false
if(empty($bind))
return false;
// assign bind
$return['bind'] = $bind;
// Implode rows
$return['cols'] = implode(",",$iCols);
// return the final data array
return $return;
}
To use:
// Make sure to include the above functions here....
// Get the uploads
$uploads = CompileUpload();
// If there are uploads and the user is logged in
if(!empty($uploads) && !empty($_SESSION['id'])) {
// Is this really correct? Do you have a table for each user?
// Compile your statement
$statement = "INSERT into `".$_SESSION['id']."` (`immagine`,`type`,`profilo`) VALUES ".$uploads['cols'];
// Get connection and prepare
// You may need to do $con = connection(); $con->prepare...etc.
// but this should work
$query = connection()->prepare($statement);
// Execute with bind values
$query->execute($uploads['bind']);
}
The sql statement would look something like this:
INSERT into `whatever` (`immagine`,`type`,`profilo`) VALUES (:0name,:0type,:0data)
Multiple uploads would be:
INSERT into `whatever` (`immagine`,`type`,`profilo`) VALUES (:0name,:0type,:0data),(:1name,:1type,:1data)
Related
I am trying to create a form that uploads a CSV file that then inserts the data into a MYSQL database. with my code, I don't get any error message, it just doesn't insert. Here is my code:
Here is the form code:
<!DOCTYPE html>
<html>
<head>
<title>CSV Upload</title>
</head>
<body>
<form method="POST" enctype="multipart/form-data" action="import.php">
<div align="center">
<p>Select CSV file: <input type="file" name="file" /></p>
<p><input type="submit" name="csv_upload_btn" value="Upload" /></p>
</div>
</form>
</body>
</html>
//Process form
if(isset($_POST["csv_upload_btn"])){
if($_FILES['file']['name']){
$filename = explode("",$_FILES['file']['name']);
if($filename[1] == "csv"){
$handle = fopen($_FILES['file']['tmp_name'], "r");
while($data = fgetcsv($handle)){
$item1 = mysqli_real_escape_string($connection, $data[0]);
$item2 = mysqli_real_escape_string($connection, $data[1]);
$item3 = mysqli_real_escape_string($connection, $data[2]);
$item4 = mysqli_real_escape_string($connection, $data[3]);
$item5 = mysqli_real_escape_string($connection, $data[4]);
$query = " INSERT INTO data(softwareID,districtID,statusID,date_approved,date_expired) VALUES('$item1', '$item2', '$item3', '$item4', '$item5') ";
$run_query = mysqli_query($connection, $query);
}
fclose($handle);
if($run_query == true){
echo "File Import Successful";
}else{
echo "File Import Failed";
}
}
}
}
//Close Connection
mysqli_close($connection);
?>
Your current code would be vulnerable to SQL Injections, I suggest using prepared statements or parameterized queries and it would probably fix your problem also. Ill show you an example on how I connect to databases (using PDO):
# You can also set this up in a function, but this is how I use it as it works best for me.
# Also best if you keep this class (or function if you choose to change it) out of the `public_html` folder and just include/require it.
class DB extends PDO{
public $connect;
public function __construct(string $db_name){
try{
# Declare your mysql credentials
$cred = [
"db_user" => "localhost",
"db_user" => "root",
"db_pass" => "xxx"
];
$this->connect = new \PDO("mysql:host=".$cred['db_host'].";dbname=".$db_name, $cred['db_user'], $cred['db_pass']);
$this->connect->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}catch(PDOException $e){
# You can include the $e variable from above in the echo below to show the error, but I chose not to
# just incase someone is trying to attack your website. That error can give them a lot of information
# about your SQL query, which can be very useful to an attacker; giving them an idea on how to formulate
# an injection (if possible).
echo("Error");
}
}
}
# Start a connection to the declared database name
$db = new DB("database_name");
# Prepare the query but refrain from inputting variables directly into it. Instead put a :placeholder in its spot like so:
$queryPrep = $db->connect->prepare("INSERT INTO `data` (softwareID, districtID, statusID, date_approved, date_expired) VALUES (:softwareID, :districtID, :statusID, :date_approved, :date_expired)");
# You then bind your value(s) into your query like so (make sure to declare what datatype your variable is in the 3rd parameter):
$queryPrep->bindValue(':softwareID', $softwareID, PDO::PARAM_STR);
$queryPrep->bindValue(':districtID', $districtID, PDO::PARAM_STR);
$queryPrep->bindValue(':statusID', $statusID, PDO::PARAM_STR);
$queryPrep->bindValue(':date_approved', $date_approved, PDO::PARAM_INT);
$queryPrep->bindValue(':date_expired', $date_expired, PDO::PARAM_INT);
# Full list of PDO::PARAM_ Predefined Constants
# https://www.php.net/manual/en/pdo.constants.php
# Now you can finally execute your query
$queryPrep->execute();
# Check to see if any rows have been added to the database from the last SQL statement
if($queryPrep->rowCount() > 0) echo "true - Row Added";
else echo "false - Row Not Added";
I also have a function that I created a while back to parse CSV files/strings into an easier useable array (always assuming the first line will be the column names though):
function csvParse($input, $callback = false){
$results = [];
$raw_array = (is_file($input)) ? array_map('str_getcsv', file($input)) : array_map('str_getcsv', explode("\n", $input));
$array = array_splice($raw_array, 1, count($raw_array));
foreach($raw_array[0] as $c) $columns[] = $c;
foreach($array as $key0 => $val0) foreach($val0 as $key1 => $val1) $results[$key0][$columns[$key1]] = $val1;
if(is_callable($callback) && !empty($results)) call_user_func_array($callback, array($results));
elseif(!empty($results)) return $results;
else throw new Exception("Results Empty: Can not read the string or open file.");
}
# Can also be file location
$input = "animal,name,age\n
goat,crimin4l,24\n
deer,henry,11\n
bear,teddy,15";
csvParse($input, function ($arr){
print_r($arr);
});
Output:
Array
(
[0] => Array
(
[animal] => goat
[name] => crimin4l
[age] => 24
)
[1] => Array
(
[animal] => deer
[name] => henry
[age] => 11
)
[2] => Array
(
[animal] => bear
[name] => teddy
[age] => 15
)
)
You could put both of them together to complete your task successfully like so:
$db = new DB("database_name");
if(isset($_POST["csv_upload_btn"]) && !empty($_FILES['file'])){
$file['base'] = basename($_FILES['file']['name']);
$file['path'] = $_FILES['file']['tmp_name'];
$file['mime'] = strtolower(pathinfo($file['base'], PATHINFO_EXTENSION));
if($file['mime'] === "csv" || $file['mime'] === "txt"){
csvParse($file['path'], function ($arr){
# Start the $queryPrep here;
# If for each CSV row you want to add a MySQL row then
# you will need a foreach loop to iterate through each
# of the array(s).
});
}else echo("Error: File must be .CSV or .TXT");
}
I'm trying to build a script where I need to read a txt file and execute some process with the lines on the file. For example, I need to check if the ID exists, if the information has updated, if yes, then update the current table, if no, then insert a new row on another temporary table to be manually checked later.
These files may contain more than 20,30 thousand lines.
When I just read the file and print some dummie content from the lines, it takes up to 40-50ms. However, when I need to connect to the database to do all those verifications, it stops before the end due to the timeout.
This is what I'm doing so far:
$handle = fopen($path, "r") or die("Couldn't get handle");
if ($handle) {
while (!feof($handle)) {
$buffer = fgets($handle, 4096);
$segment = explode('|', $buffer);
if ( strlen($segment[0]) > 6 ) {
$param = [':code' => intval($segment[0])];
$codeObj = Sql::exec("SELECT value FROM product WHERE code = :code", $param);
if ( !$codeObj ) {
$param = [
':code' => $segment[0],
':name' => $segment[1],
':value' => $segment[2],
];
Sql::exec("INSERT INTO product_tmp (code, name, value) VALUES (:code, :name, :value)", $param);
} else {
if ( $codeObj->value !== $segment[2] ) {
$param = [
':code' => $segment[0],
':value' => $segment[2],
];
Sql::exec("UPDATE product SET value = :value WHERE code = :code", $param);
}
}
}
}
fclose($handle);
}
And this is my Sql Class to connect with PDO and execute the query:
public static function exec($sql, $param = null) {
try {
$conn = new PDO('mysql:charset=utf8mb4;host= '....'); // I've just deleted the information to connect to the database (password, user, etc.)
$q = $conn->prepare($sql);
if ( isset($param) ) {
foreach ($param as $key => $value) {
$$key = $value;
$q->bindParam($key, $$key);
}
}
$q->execute();
$response = $q->fetchAll();
if ( count($response) ) return $response;
return false;
} catch(PDOException $e) {
return 'ERROR: ' . $e->getMessage();
}
}
As you can see, each query I do through Sql::exec(), is openning a new connection. I don't know if this may be the cause of such a delay on the process, because when I don't do any Sql query, the script run within ms.
Or what other part of the code may be causing this problem?
First of all, make your function like this,
to avoid multiple connects and also o get rid of useless code.
public static function getPDO() {
if (!static::$conn) {
static::$conn = new PDO('mysql:charset=utf8mb4;host= ....');
}
return static::$conn;
}
public static function exec($sql, $param = null) {
$q = static::getPDO()->prepare($sql);
$q->execute($param);
return $q;
}
then create unique index for the code field
then use a single INSERT ... ON DUPLICATE KEY UPDATE query instead of your thrree queries
you may also want to wrap your inserts in a transaction, it may speed up the inserts up to 70 times.
This question already has answers here:
PHP PDO Insert Using Loop
(2 answers)
Closed 5 months ago.
foreach($parentinfojson as $value) {
if (!empty($parentinfojson )) {
$stmt2 = $dbh -> prepare("INSERT INTO parentinfo (last_name,first_name,status) VALUES (:lastname,:firstname,:status)");
$stmt2_ = $stmt2 -> execute(array(':firstname' => $value['firstname'], ':lastname' => $value['lastname'], ':status' => $status));
} else {
$stmt2_ = $stmt2 -> execute();
}
}
if ($stmt2_ && $stmt3_ && $stmt1_ && $stmt_ && $stmt5_ && $stmt4_) {
echo json_encode(array(
'error' => false,
'message' => "Added"
));
}
This is my execute in Inserting new data in the table. When i tested the adding of empty data(parentinfojson is empty) i get error that Notice: Undefined variable: stmt2_. What i did is i added an else statement and i initialize the variable still i get error. I tried to echo something in the else statement as well but i get error. Now I run out of idea on how to initialize the variable when the json is empty so that i dont get the error undefined variable
You just defined $stmt2 inside the loop, if $parentinfojson is empty it'll certainly get undefined. Why not define/initialize it.
// initialize up top
$stmt_ = $stmt1_ = $stmt2_ = $stmt3_ = $stmt4_ = $stmt5_ = false;
$stmt2 = $dbh->prepare("INSERT INTO parentinfo (last_name,first_name,status) VALUES (:lastname,:firstname,:status)");
foreach($parentinfojson as $value) {
$stmt2_ = $stmt2->execute(array(
':firstname' => $value['firstname'],
':lastname' => $value['lastname'],
':status' => $status
));
}
if ($stmt2_ && $stmt3_ && $stmt1_ && $stmt_ && $stmt5_ && $stmt4_) {
echo json_encode(array(
'error' => false,
'message' => "Added"
));
}
Sidenote: Another way would be to build the query dynamically, including the placeholders and the values. So that in turn, you don't have to loop each batches of insert but instead, creating the SQL batch insert then binding all of the values into one single insert invocation:
$stmt_ = $stmt1_ = $stmt2_ = $stmt3_ = $stmt4_ = $stmt5_ = false;
if(!empty($parentinfojson)) {
$base_query = 'INSERT INTO parentinfo (last_name, first_name, status) VALUES ';
$placeholders = implode(',', array_map(function($batch){
return '(' . implode(',', array_fill(0, count($batch), '?')) . ')';
}, $parentinfojson));
$base_query .= $placeholders;
$parentinfojson = call_user_func_array('array_merge', array_map('array_values', $parentinfojson));
$stmt2 = $dbh->prepare($base_query);
$stmt2_-> $stmt2->execute($parentinfojson);
}
There might be a chance that the json variable you are receiving is not empty, so you should also add a check for valid json, this is the function to check a valid json
function isJson($string) {
json_decode($string);
return (json_last_error() == JSON_ERROR_NONE);
}
I have data (exact) from this HTTP POST:
rowno=1.00000000&date_line=2014-10-07&name=Dan%20Volunteer&affiliation=Enterprise&checkno=1701&amount=20025.00000000&total=20250.00000000¬es=&date_deposit=&rowno=2.00000000&date_line=2014-10-07&name=Harper%20Lee&affiliation=Enterprise%20B&checkno=1702&amount=225
then this code to process
<?php
file_get_contents("php://input");
$db = null;
if (isset($_SERVER['SERVER_SOFTWARE']) &&
strpos($_SERVER['SERVER_SOFTWARE'],'Google App Engine') !== false) {
// Connect from App Engine.
try{
$db = new pdo('mysql:unix_socket=/cloudsql/wonder:bread;dbname=loaf', 'root', '');
}catch(PDOException $ex){
die(json_encode(
array('outcome' => false, 'message' => 'Unable to connect.')
)
);
}
};
try {
if (array_key_exists('name', $_POST)) {
$stmt = $db->prepare('INSERT INTO entries (name, affiliation) VALUES (:name, :affiliation)');
$stmt->execute(array(':name' => htmlspecialchars($_POST['name']), ':affiliation' => htmlspecialchars($_POST['affiliation'])));
$affected_rows = $stmt->rowCount();
// Log $affected_rows.
}
} catch (PDOException $ex) {
// Log error.
}
$db = null;
?>
<?php
header("Content-type: application/vnd.fdf");
// read and store the data however you want
// reply with some FDF data
echo <<<RESPONSE
%FDF-1.2
1 0 obj
<< /FDF <<
/Status (Wham bam! File sent.)
>>
>>
endobj
trailer
<< /Root 1 0 R >>
%%EOF
RESPONSE;
?>
This http post has two records (row/recount count always varies), but only data from the last row is being inserted. Need all rows.
I'm going to stab at this one....I think what is happening is that you are just processing the return post as is, so the first rowno is being skipped over (rather the second rowno is overwriting the first). If you receive that post back as a string, you need to split it by preg_match() or explode() so that you can loop over it with your try.
Try this class on your string. This class will split the string into arrays based on rows. Then you need to take the resulting array $insert then process each array in your an sql loop...does that make sense?
class ProcessPost
{
public static function Split($value = '',$splitVal = 'rowno=')
{
if(!empty($value)) {
// Explode by row values
$rows = explode($splitVal,$value);
$rows = array_filter($rows);
if(is_array($rows) && !empty($rows)) {
foreach($rows as $_row => $querystring) {
parse_str($splitVal.$querystring,$_array[]);
}
foreach($_array as $row_key => $row_val) {
if(empty($row_val))
unset($_array[$row_key]);
}
return $_array;
}
}
}
}
$test = 'rowno=1.00000000&date_line=2014-10-07&name=Dan%20Volunteer&affiliation=Enterprise&checkno=1701&amount=20025.00000000&total=20250.00000000¬es=&date_deposit=&rowno=2.00000000&date_line=2014-10-07&name=Harper%20Lee&affiliation=Enterprise%20B&checkno=1702&amount=225';
$insert = ProcessPost::Split($test);
xxxHow can I fix this script so that it actually posts 3 separate images rather than the same image 3 times. Any help would be much appreciated. I will provide the code html code to encase that's relevant.
html:
<form method="post" action="insert.php" enctype="multipart/form-data">
<input type="text" name="caseName"><br>
<input type="file" name="upload[]"/>
<input type="file" name="upload[]"/>
<input type="file" name="upload[]"/>
<input type="submit" value="Submit" >
</form>
php:
if ( isset( $_FILES['upload'] ) ) {
$name_array = $_FILES['upload']['name'];
$tmp_name_array = $_FILES['upload']['tmp_name'];
for ( $i = 0; $i < count( $name_array ); $i++ ) {
if ( move_uploaded_file( $tmp_name_array[$i], "uploaded/" . $name_array[$i] ) ) {
echo $name_array[$i];
} else {
echo "failed";
}
}
$dsn = 'mysql:host=localhost;dbname=GLO12408958DB';
$username = 'root';
$password = 'yt987210d';
//
// DB connection was made
//
$pdo = new PDO($dsn, $username, $password);
//loop over array to get names. Make sure we have actual content.
if ( count( $name_array ) > 0 && $name_array !== false ) {
//Prepare query
$statement = $pdo->prepare( 'INSERT INTO caseStudies(caseImage,caseImage2,caseImage3) VALUES (?,?,?)' );
//use a different index in the event that the numeric keys in the name array are not ordered correctly
$index = 1;
foreach ( $name_array as $key => $filename ) {
$statement->bindParam( $index, $filename, PDO::PARAM_STR );
$index++;
}
$statement->execute();
//etc....
}
}
According to the manual, bindParam() "Binds a PHP variable to a corresponding named or question mark placeholder in the SQL statement that was used to prepare the statement. Unlike PDOStatement::bindValue(), the variable is bound as a reference and will only be evaluated at the time that PDOStatement::execute() is called."