0

I am working on a school assignment and I need to process a form with values such as location, price, description, and a 1 to 4 images up to 5MB each. I need to upload to a database, but I cannot get the images sent using send_long_data(). I do not know how to process only some of the inputs as send long data. I have been able to cobble together code for binding an array of inputs by reference using call_user_func_array(), but for over a day now I have had no luck getting it to work. New to coding and I am stumped. Any help is appreciated.

**<?php



//show logged in/logout header
include 'header.php';


include 'connectLankasListDB.php';

$image_count = 0;


if(!isset($_SESSION['username'])) {

    echo ('Please login before posting.');
    sleep(2);
    header('Location: login.html');




} elseif (empty($_POST['title']) or 
        empty($_POST['price']) or
        empty($_POST['description']) or
        empty($_POST['email']) or
        empty($_POST['confirm_email'])) {

    //All fields not filled out
    echo 'Please fill out title, price, description, email, and email-confirmation fields.';
    echo '<br/>';
    echo '<a href="new_post.html">New Post</a>';


//email not a valid email, prompt to enter correct email    
} elseif (filter_var($_POST['email'], FILTER_VALIDATE_EMAIL) === false) {
    die("Enter a valid email address.");


//terms agreement checkbox not checked
} elseif ($_POST['terms'] === '0') {
    die("Please agree to terms and conditions");


//email and confirmation email match, continue with script   
} elseif ($_POST['email'] !== $_POST['confirm_email']) {
    echo 'Email and confirmation email do not match--try again!';


//Check that image files are correct type and within the size limit   
} elseif (isset($_FILES['images1'])) {
    //print_r($_FILES);
    $image_count = count(array_filter($_FILES['images1']['tmp_name'])); //number of uploaded images
    $allowed_extensions = array("jpg", "jpeg", "png", "gif", "bmp");
    for ($x=0; $x < $image_count; $x++) {

        $file_name = $_FILES['images1']['name'][$x];
        $file_size = $_FILES['images1']['size'][$x];
        $file_tmp = $_FILES['images1']['tmp_name'];


        //$ext = substr($file_name, strlen($file_name)-4,strlen($file_name));
        $ext = explode(".", $file_name);
        $file_ext = strtolower(end($ext));
        echo $file_ext;


        if (!in_array($file_ext, $allowed_extensions)) {
            die("Only jpg, jpeg, png, gif, and bmp files allowed!");

        } elseif ($file_size > 5000000) {
            die("File size limit (5MB) exceed!");
        }



    }
}

    //user has filled in all required fields
    //validate and sanitize, store to variables
    $sub_category = filter_input(INPUT_POST, 'sub_category', FILTER_SANITIZE_STRING);
    $location = filter_input(INPUT_POST, 'location', FILTER_SANITIZE_STRING);
    $title = filter_input(INPUT_POST, 'title', FILTER_SANITIZE_STRING);
    $price = filter_input(INPUT_POST, 'price', FILTER_SANITIZE_NUMBER_FLOAT);
    $description = filter_input(INPUT_POST, 'description', FILTER_SANITIZE_STRING);
    $email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
    $timestamp = date("Y-m-d H:i:s");
    $terms = '1';


    //retrieve Location_ID from Location table for Posts table
    $sql1 = "SELECT Location_ID FROM Location WHERE LocationName = '$location'";
    $query1 = mysqli_query($conn, $sql1) or die(mysqli_error($conn));

    $result1 = mysqli_fetch_assoc($query1);
    $location_id = $result1["Location_ID"];

    //retrieve Subcategory_ID from SubCategory table for Posts table
    $sql2 = "SELECT SubCategory_ID FROM SubCategory WHERE SubCategoryName = '$sub_category'";
    $query2 = mysqli_query($conn, $sql2);

    $result2 = mysqli_fetch_assoc($query2);
    $subcategory_id = $result2["SubCategory_ID"];

    /*   
    //save to Posts table
    mysqli_query($conn, "INSERT INTO Posts
                (Location_ID, title, price, description, email, SubCategory_ID, TimeStamp, Agreement)
                VALUES
                ('" . $location_id . "', '" . $title . "', '" . $price . "', '". $description . "', '" . $email ."',"
                . "'" . $subcategory_id ."', '" . $timestamp ."', '" . $terms . "')")
                OR die(mysqli_error($conn));*/

    **//query for insert with no images
    $ins_query_fields = "Location_ID, Title, Price, Description, Email, SubCategory_ID,TimeStamp, Agreement";
    $ins_query_vals = "?,?,?,?,?,?,?,?";
    $type_args = "ssssssss";
    $bind_vars = array($location_id, $title, $price, $description, $email, $subcategory_id, $timestamp, $terms);


    $tmp_name_array = array();  
    $pic_name_array = array();

    //print_r($_FILES['images1']['tmp_name']);
    //prepare query based on number of images
    if ($image_count > 0) {
        $i = 1;

        for($n = 0; $n < $image_count; $n++) {
            $ins_query_fields .= ", Image_" . $i;
            array_push($pic_name_array, "Image_". $i);
            $ins_query_vals .= ",?";
            $type_args .= "s";
            ${"Image_". $i} = $_FILES['images1']['tmp_name'][$n];
            array_push($tmp_name_array, ${"Image_". $i}); 
            $i++;
        }

        $bind_vars = array_merge($bind_vars, $tmp_name_array);
    }    

    **//save image files to Posts table
    ///////////////////////////////////////
    $stmt = $conn->prepare("INSERT INTO Posts($ins_query_fields) VALUES($ins_query_vals)");

    //    
    //bind params by reference
    $inputArray[] = &$type_args;
    $j = count($bind_vars);
    for($i=0; $i < $j; $i++) {
        $inputArray[] = &$bind_vars[$i];
    }

    //print_r($inputArray);

    //use call_user_func_array
    call_user_func_array(array($stmt, 'bind_param'), $inputArray);

    //$stmt->execute();
    //print_r($bind_vars);
    print_r($tmp_name_array);
    if ($image_count > 0) {
        $index = count($bind_vars) - $image_count - 1;
        for($i = 0; $i < $image_count; $i++) {
            $contents = $tmp_name_array[$i];
            //$fp = fopen($bind_vars[($index) + $i], "r");
            $fp = fopen($contents, "r");
            $size = 0;

            while ($data = fread($fp, 1024)) {

                $size += strlen($data);
                $stmt->send_long_data($index, $data);

            }

        }
    }


    if ($stmt->execute()) {

    } else {
        die($conn->error);
    }****



    echo 'Your post has been saved.<br/>';
    echo '<br/>';        
    echo '<a href="index.php">Go to Main Page</a>';



?>**

Ok, I tried to separate the data upload, which is a fixed number of variables, and the image blob uploads, which i have tried doing with a loop. The first data posts, the images do not. Is this even workable this way? There must be some fundamental principle about this that I am not understanding. Here is the revised code, omitting the validation steps.

``if ($image_count > 0) {
    $i = 1;
    $pic_query_holder_x = "";
    $tmp_name_array = array();  
    $pic_in_fields = array();
    $pic_type_args = "";
    for($n = 0; $n < $image_count; $n++) {
        //$ins_query_fields .= ", Image_" . $i;
        array_push($pic_in_fields, "Image_". $i);
        $pic_query_holder_x .= ",?";
        $pic_type_args .= "s";
        ${"Image_". $i} = $_FILES['images1']['tmp_name'][$n];
        array_push($tmp_name_array, ${"Image_". $i}); 
        $i++;
    }
    $pic_query_holder = ltrim($pic_query_holder_x, ',');
    $pic_bind_vars = $tmp_name_array;
    echo '<br/>';
    echo $pic_query_holder;
    echo '<br/>';
    print_r($tmp_name_array);
}    

//save image files to Posts table
///////////////////////////////////////
$stmt = $conn->prepare("INSERT INTO Posts($ins_query_fields) 
VALUES($ins_query_vals)");

//    
//bind params by reference
$inputArray[] = &$type_args;
$j = count($bind_vars);
for($i=0; $i < $j; $i++) {
    $inputArray[] = &$bind_vars[$i];
}

//use call_user_func_array
call_user_func_array(array($stmt, 'bind_param'), $inputArray);
$stmt->execute();

//$index = count($bind_vars) - $image_count -1;
//$fp = fopen($tmp_name_array[$index], "r");


//$stmt->execute();
//print_r($pic_in_fields);
//print_r($pic_query_holder);
if ($image_count > 0) {


    //bind params
    $in_array[] = &$pic_type_args;
    $k = count($tmp_name_array);
    for ($i=0; $i < $k; $i++) {
        $in_array[] = &$tmp_name_array[$i];
    }

    //$index = count($tmp_name_array) - $image_count - 1;
    for($i = 0; $i < $image_count; $i++) {
        //prepare statement
        $go_pics = $conn->prepare("INSERT INTO Posts($pic_in_fields[$i]) VALUES(?)");
        $contents = $tmp_name_array[$i];
        //$fp = fopen($bind_vars[($index) + $i], "r");
        $fs = fopen($contents, "r");
        $size = 0;

        while ($data = fread($fs, 1024)) {

            $size += strlen($data);
            $go_pics->send_long_data($i, $data);

        }
    //print_r($in_array);
    $go_pics->execute();   
    }

}
enter code here
Al T
  • 23
  • 3
  • 1
    You need to use `move_uploaded_file()` to save uploaded images to your server. – Karlo Kokkak Apr 10 '18 at 00:36
  • `$file_tmp = $_FILES['images1']['tmp_name'];` needs to be `$file_tmp = $_FILES['images1']['tmp_name'][$x];` as you're uploading an array of files. – Karlo Kokkak Apr 10 '18 at 00:39
  • I would do it that way, but our assignment included a schema that requires they be stored as blob. thanks. – Al T Apr 10 '18 at 00:44
  • Possible Duplicate of that: https://stackoverflow.com/questions/6472233/can-i-store-images-in-mysql – Karlo Kokkak Apr 10 '18 at 00:47
  • Not a duplicate. The question is how to post the blobs that must be sent using send_long_data(), must be bound with call_user_func_array() due to varying number of images, and includes other types of data. Have been searching for a while but have not found a way, if there is one. Or can I post each with a loop, using send_long_data only on the blobs, all to the same Post_ID, maybe? – Al T Apr 10 '18 at 00:54
  • 1
    Yes, insert non blobs and then update with blobs after. Or check out page I linked below which may help also – Nerdi.org Apr 10 '18 at 01:07

2 Answers2

0
<?php
$stmt = $mysqli->prepare("INSERT INTO messages (message) VALUES (?)");
$null = NULL;
$stmt->bind_param("b", $null);
$fp = fopen("messages.txt", "r");
while (!feof($fp)) {
 $stmt->send_long_data(0, fread($fp, 8192));
}
fclose($fp);
$stmt->execute();
?>

This is straight from the PHP manual. Feof() is just there to ensure that the file exists and was opened properly. With a proper loop, you can access the image file and insert the blob into the database. Alternatively, you can prepare a series of blobs and do your database update or insert separately. Either way, I think this code should get you in the right direction.

Just run this segment to insert or update with blob data after you handle the non blobs.

Also, this SO page may help you too. Insert Blobs in MySql databases with php

Nerdi.org
  • 895
  • 6
  • 13
  • Can this work if not all of the values to be sent are blobs? There would be no file to open then. As an example, if I have 5 strings and 3 images, i only need the send_long_data for the images, and that number can vary. I thought I had a working loop but not the case. It falls apart when the files get too big. I think the reference to the $_FILES tmp name location that I bound to the parameters sent the files before the loop. How do i keep that from happening if I have to bind a variable for each values placeholder? – Al T Apr 10 '18 at 01:30
0

You can try tinker with this working template below. This uses send_long_data() to upload images to database. This adds a record with columns of different datatypes in just one query.

Didn't use call_user_func_array() as I don't think it's needed.

<?php
    $conn = new mysqli("127.0.0.1", "root", "", "db");

    if(isset($_POST['submit'])) {
        $null1 = NULL;
        $null2 = NULL;
        $null3 = NULL;
        $null4 = NULL;
        $title = isset($_POST['title']) ? $_POST['title'] : '';
        $email = isset($_POST['email']) ? $_POST['email'] : '';

        $stmt = $conn->prepare("INSERT INTO Posts (Image_1, Image_2, Image_3, Image_4, title, email ) VALUES (?,?,?,?,?,?);");
        $stmt->bind_param("bbbbss", $null1, $null2, $null3, $null4, $title, $email );

        for($i = 0; $i < count( $_FILES['images1']['tmp_name'] ); $i++) {
            if(!empty($_FILES['images1']['tmp_name'][$i])) {
                $target_file = $_FILES['images1']['tmp_name'][$i];

                $fp = fopen($target_file, "r");
                while (!feof($fp)) {
                    $stmt->send_long_data($i, fread($fp, 8192));
                }
                fclose($fp);
                unlink($_FILES['images1']['tmp_name'][$i]);
                echo "Uploading image blob success!<br/>\n";
            }
        }

        $stmt->execute();
        $stmt->close();
        $conn->close();
    }
?>

<form action="" method="post" enctype="multipart/form-data">
 <input type="text" name="title" value="this is the title"/><br/>
 <input type="text" name="email" value="john@yahoo.com"/><br/>
 <input type="file" name="images1[]" value=""/><br/>
 <input type="file" name="images1[]" value=""/><br/>
 <input type="file" name="images1[]" value=""/><br/>
 <input type="file" name="images1[]" value=""/><br/><br/>
 <input type="submit" name="submit" value="Upload Data"/>
</form>
Karlo Kokkak
  • 3,674
  • 4
  • 18
  • 33
  • Thanks! I'll give it try tomorrow. – Al T Apr 10 '18 at 05:37
  • I need to use call_user_func_array() because the number of file, and hence parameters will vary. In this code I only set up 4, but I should be able to change that without affecting the code. I – Al T Apr 12 '18 at 06:59
  • The code can accept any number of files so long as it doesn't exceed the maximum limit of 4. If you want to increase the limit, just add another column in database of the same name format. – Karlo Kokkak Apr 12 '18 at 07:07
  • "and a 1 to 4 images" – Karlo Kokkak Apr 12 '18 at 07:09
  • Yes, Mr. Blue, my mistake. That was the original intent, but I will need to increase that number in a future implementation not related to this assignment. I finally got it to work without a limit, thanks to your example. I was not setting the $Image_1, $Image_2... to NULL, and so a value was being inserted before the loop could run. Thanks for your help! – Al T Apr 13 '18 at 21:55