Upload CSV and Import into MySQL Database - php

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");
}

Related

PHP Improve performance to execute multiple queries while reading a file with thousand lines

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.

drop down value not being submitted to post array

Currently working with a php project in which I connect to a database in phpmyadmin. I'm currently implementing my one to many relationship and on one of my forms there is a drop down list with the list of categorys(foreign key) that a product in my database can have, however when I check the post array, and the array that contains all the values for the insert query everything is there except the foreign key.
Drop down list and array of values:
<select name="Category_ID">
<?php
foreach ($category as $cat) {
echo '<option value="' . $cat['ID'] . '">' . $cat['ID'] . '</option>';
}
?>
</select>
Array (
[authorName] => Hiroshi Sakurazaka
[bookName] => All You Need Is Kill
[costPrice] => 59
[sellPrice] => 99
[productCatID] => )
Could not insert book
Heres the file that converts the data in the formdata array into an object:
<?php
require_once 'product.php'; //Connecting to the product class
require_once 'Classes/productTable.php'; //Connecting to the TableGateway
require_once 'Classes/Connection.php'; //Connecting to the Connection class
require_once 'validateProduct.php';//Connecting to the product validation
require_once 'utils/functions.php';
//start_session();
//
//if (!is_logged_in()) {
// header("Location: login_form.php");
//}
echo '<pre>';
print_r($_POST);
echo '</pre>';
$formdata = array();
$errors = array();
validate($formdata, $errors);
if (empty($errors)) {
$AuthorName = $formdata['AuthorName'];
$BookName = $formdata['BookName'];
$costPrice = $formdata['CostPrice'];
$sellPrice = $formdata['sellPrice'];
$productCatID = $formdata['productCatID'];
echo '<pre>';
print_r($formdata);
echo 'Form Data array';
echo '</pre>';
$connection = Connection::getInstance();
$gateway = new productTable($connection);
$id = $gateway->insert($AuthorName, $BookName, $costPrice, $sellPrice, $productCatID);
header('Location: viewProducts.php');
}
else {
require 'createProductForm.php';
}
Heres the function in the table gateway that inserts the object into the database:
> public function insert($authorName, $bookName, $costPrice, $sellPrice,
> $productCatID) {
> $sql = "INSERT INTO "
> . "`product`(`AuthorName`, `BookName`, `CostPrice`, `sellPrice`, `productCatID`)"
> . " VALUES (:authorName,:bookName,:costPrice,:sellPrice,:productCatID)";
> $statement = $this->connection->prepare($sql);
> $params = array(
> "authorName" => $authorName,
> "bookName" => $bookName,
> "costPrice" => $costPrice,
> "sellPrice" => $sellPrice,
> "productCatID" => $productCatID
> );
> print_r($params);
> $status = $statement->execute($params);
>
> if (!$status) {
> die("Could not insert book");
> }
>
> $id = $this->connection->lastInsertId();
>
> return $id; }
can somebody please tell me what I'm missing?
Your select has the name of Category_ID not productCatID. If you expecting GET/POST data coming in under productCatID you need to name your select productCatID.
Solved my problem finally, so I'll post how I did it. For debuging my code and to see what values were being passed into the $POST array and the $formdata array, I used print_r to post each array if there was a problem and heres what I got:
$POST Array
(
[AuthorName] => g
[BookName] => g
[CostPrice] => 33
[sellPrice] => 3
[productCatID] => 4
[createProduct] => Create Product
)
form data array
(
[AuthorName] => g
[BookName] => g
[CostPrice] => 33
[sellPrice] => 3
[ProductCatID] =>
)
As you can see the $POST array was getting the value from the drop down list just fine, it was the form data array that was the issue. Embarrassingly the issue was just a simple typo error that was quickly resolved in my validation script.
With foreach you have to explicitly request access to the key, so if you don't, you'll only get the array values.
Just do this to debug (outside of the <select>):
foreach($category as $key=>$cat){
var_dump($cat['ID'], $cat, $key);
}
And I think you'll see where the actual data you need is.
(also, you seem to be running without strict errors and notices on, which is crucial for debugging and might show you some notices when array keys you try to access don't exist)

Receiving a Fatal Error on multiple file upload [duplicate]

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)

MySQL INSERT INTO from PHP://INPUT multiple rows

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&notes=&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&notes=&date_deposit=&rowno=2.00000000&date_line=2014-10-07&name=Harper%20Lee&affiliation=Enterprise%20B&checkno=1702&amount=225';
$insert = ProcessPost::Split($test);

How to insert "Null" value in MySQL Database Cell if no Files are added?

I have a form that sends input data to corresponding field on MYSQL database.Right now I have it setup where any files that are uploaded are packaged in a zip and placed in the "uploads" folder while it sends to the database the file name/location. How do I make it where when the user uploads no files it sends to the database as NULL result in the corresponding database "file" cell and no zip file is sent to the "uploads" folder ?
**UPDATE: OK I started with suggested answer but don't get "NULL" when no file is uploaded just the usual that I have written.I tried an if/else statement and it's the same. What am i doing wrong?
This what the array looks like when no file is uploaded: Array (
[name] => Array ( [0] => ) [type] => Array ( [0] => ) [tmp_name] =>
Array ( [0] => ) [error] => Array ( [0] => 4 ) [size] => Array ( [0]
=> 0 ) )
The error field contains 4. i think this might the reason why its not working.I don't understand why this error field has this number. Any suggestions please?
PHP
<?php
$project = $_POST['project'];
$assignto = $_POST['assignto'];
$asdate = $_POST['asdate'];
$chdate = $_POST['chdate'];
$ddate = $_POST['ddate'];
$notes = $_POST['notes'];
$asdate=date('Y-m-d', strtotime($asdate));
$chdate=date('Y-m-d', strtotime($chdate));
$ddate=date('Y-m-d', strtotime($ddate));
$timestamp = time();
if (isset ($_POST['submit']))
{
$filesArray= $_FILES["files"];
for ($num=0; $num<count($filesArray["name"]);$num++)
{
$fileName = $filesArray["name"][$num];
$tempName= $filesArray["tmp_name"][$num];
move_uploaded_file($tempName,"tmp/".$fileName);
}
$archiveName= $timestamp.".zip";
$filesArrayNames= $_FILES["files"]["name"];
$zipsDir= scandir ("uploads/");
$error = false;
foreach($zipsDir as $zipDirfile)
{
if($zipDirfile == $archiveName)
{
$error= true ;
break;
}
}
if ($error== false)
{
$tmpDir = scandir ("tmp/");
$zip = new ZipArchive;
$zip->open("uploads/".$archiveName, ZipArchive::CREATE);
for ($num =0; $num<count($filesArray["name"]);$num++)
{
$fileName = $filesArray["name"][$num];
foreach($tmpDir as $tmpDirfile)
{
if($tmpDirfile == $fileName)
{
$zip->addFile("tmp/".$fileName);
echo " Adding: ".$fileName."<br/>";
}
}
}
$zip->close();
for ($num=0; $num<count($filesArray["name"]);$num++)
{
$fileName = $filesArray["name"][$num];
foreach($tmpDir as $tmpDirFile)
{
if($tmpDirfile == $fileName)
{
unlink("tmp/".$fileName);
}
}
}
}
else
{
echo "Name already exists";
}
} if (!empty($filesArray)) {
$filepath = "NULL";
}else
{$filepath = addslashes("https://docs.google.com/viewer?url=www.art.com/uploads/".$archiveName.""); }
print_r($filepath);
mysql_connect("tre.com","amadast","ufco1954") or die ('Error:' .mysql_error());
//database connection
mysql_select_db("mediamanagement");
$sql = "INSERT INTO demo (`name`, `id_continent`, `lastvisit`, `cdate`, `ddate`, `file`,`notes`)
VALUES ('".$project."', '".$assignto."','".$asdate."','".$chdate."','".$ddate."','".$filepath."','".$notes."')";
mysql_query($sql);
header('Location: edit-projects.html');
?>
Where you create the $filepath variable, simply check whether you have files uploaded, and if you don't use "NULL" as the content.
For example:
$filepath = addslashes("https://docs.google.com/viewer?url=www.amada-stage.com/uploads/".$archiveName."");
if (empty($filesArray)) {
$filepath = "NULL";
}
You can do a similar thing to prevent the upload occuring:
Change
if ($error== false)
to
if ($error == false && !empty($filesArray))
BTW: I hope those aren't real database credentials in your mysql_connect line!

Categories