简体   繁体   中英

Uploading multiple images to MySql database

I am trying to upload multiple images for a product for an eCommerce website. The idea is to save the service name in the services table while the images are saved in the service_images table, but whenever I run the php file, it uploads the service to the services table but only uploads one image to the service_images table instead of all the images. How can I get it to upload one service in the services table and also multiple images of that one service in the service_images table?

Below is my code:

add-service.inc.php

<?php

if (isset($_POST['add-service'])) {

    require 'config.php';

    $shop_name = mysqli_real_escape_string($conn, $_POST['shop_name']);
    $service_cat = mysqli_real_escape_string($conn, $_POST['service_cat']);
    $service_name = mysqli_real_escape_string($conn, $_POST['service_name']);
    $service_desc = mysqli_real_escape_string($conn, $_POST['service_desc']);
    $service_price = mysqli_real_escape_string($conn, $_POST['service_price']);
    $service_type = mysqli_real_escape_string($conn, $_POST['service_type']);
    $service_images = $_FILES['service_images'];
    
    if (empty($shop_name) || empty($service_cat) || empty($service_name) || empty($service_desc) || empty($service_price) || empty($service_type)) {
        header('Location: ../services.php?error=emptyFields');
        exit();
    } elseif (!preg_match('/^[a-zA-Z0-9]*$/', $shop_name) && !preg_match('/^[a-zA-Z0-9\s]*$/', $service_name) && !preg_match('/^[a-zA-Z0-9\s \. \-]*$/', $service_desc) && !preg_match('/^[0-9\.]*$/', $service_price) && !preg_match('/^[a-zA-Z0-9\s \.]*$/', $service_type)) {
        header('Location: ../services.php?error=invalidInputs');
        exit();
    } elseif (!preg_match('/^[a-zA-Z0-9]*$/', $shop_name)) {
        header('Location: ../services.php?error=invalidShopName');
        exit();
    } elseif (!preg_match('/^[a-zA-Z0-9\s]*$/', $service_name)) {
        header('Location: ../services.php?error=invalidserviceName');
        exit();
    } elseif (!preg_match('/^[a-zA-Z0-9\s \. \-]*$/', $service_desc)) {
        header('Location: ../services.php?error=invalidDescription');
        exit();
    } elseif (!preg_match('/^[0-9\.]*$/', $service_price)) {
        header('Location: ../services.php?error=invalidPrice');
        exit();
    } elseif (!preg_match('/^[a-zA-Z0-9\s \.]*$/', $service_type)) {
        header('Location: ../services.php?error=invalidStyle');
        exit();
    } else {
        foreach ($_FILES["service_images"]["tmp_name"] as $key => $tmp_name) {
            $file_name = $_FILES["service_images"]["name"][$key];
            $file_type = $_FILES["service_images"]["type"][$key];
            $file_tempName = $_FILES["service_images"]["tmp_name"][$key];
            $file_error = $_FILES["service_images"]["error"][$key];
            $file_size = $_FILES["service_images"]["size"][$key];
    
            $a = count($_FILES['service_images']['name']);
            for ($i = 0; $i < $a; $i++) {
                $fileExt = explode('.', $file_name);
                $fileActualExt = strtolower(end($fileExt));
    
                $allowed = array('jpg', 'png', 'jpeg');
    
                if (in_array($fileActualExt, $allowed)) {
                    if ($file_error === 0) {
                        if ($file_size <= 15000000) {
    
                            $newFileName = preg_replace('/\s+/', '', $service_name) . $i . '.' . $fileActualExt;
                            echo $newFileName . "<br>";
                            $fileDestination = '../../services/' . $newFileName;

                            $sql_images = "INSERT INTO service_images (shop_name, service_name) VALUES ('$shop_name', '$service_name')";
                            $result = mysqli_query($conn, $sql_images);

                            $sql = "INSERT INTO services (shop_name, service_cat, service_name, service_desc, service_price, service_type) VALUES (?,?,?,?,?,?)";
                            $stmt = mysqli_stmt_init($conn);
                            if (!mysqli_stmt_prepare($stmt, $sql)) {
                                header("Location: ../services.php?error=SaveError");
                                exit();
                            } else {
                                mysqli_stmt_bind_param($stmt, 'ssssss', $shop_name, $service_cat, $service_name, $service_desc, $service_price, $service_type);
                                mysqli_stmt_execute($stmt);

                            // move_uploaded_file($file_tempName = $_FILES["service_images"]["tmp_name"][$i], $fileDestination);
                                header("Location: ../services.php?success");
                                exit();
                            }
    
                        } else {
                            header('Location: ../services.php?error=invalidSize');
                            exit();
                        }
                    } else {
                        header('Location: ../services.php?error=invalidImage');
                        exit();
                    }
                } else {
                    header('Location: ../services.php?error=invalidImageType');
                    exit();
                }
            }
        }
    }

}

form

<form action="../admin/includes/add-service.inc.php" method="post" enctype="multipart/form-data">
   <input type="text" name="shop_name" id="shopName" class="form-input" placeholder="Shop Name">
   <select name="service_cat" id="serviceCat" class="form-input">
      <option> -- select category -- </option>
      <?php
         $sql = "SELECT * FROM service_category";
         $result = mysqli_query($conn, $sql);
         if (mysqli_num_rows($result) > 0) {
            while ($row = mysqli_fetch_assoc($result)) {
     ?>
            <option value="<?php echo $row['service_cat'] ?>"><?php echo $row['service_cat'] ?></option>
     <?php
           }
         }
     ?>
   </select>
   <input type="text" name="service_name" id="serviceName" class="form-input" placeholder="Service Name">
   <textarea name="service_desc" id="service_desc" cols="1" rows="5" placeholder="Description" class="form-input"></textarea>
   <input type="text" name="service_price" id="servicePrice" class="form-input" placeholder="Service Price">
   <input type="text" name="service_type" id="serviceType" class="form-input" placeholder="Service Type">
   <hr>
   <label for="serviceImages">*Select all pictures for your service</label>
   <input type="file" name="service_images[]" id="serviceImages" class="form-input" multiple>
   <button type="submit" class="btn-add" name="add-service">Add Service</button>
</form>

First of all, you have the same loop twice. First as foreach and then as for. Since you need numeric keys from this weird array type of $_FILES, then your best approach is to use for loop only. These double loops are already so messy, that could cause unexpected issues, if one of the files has a problem for example.

But, your main issue is, that you are basically checking only one image and then uploading it. If the validation process or success goes trough, it has exit(); at the end. It kills not only the loop, but the entire script. You are not allowing the second image loop to continue, as first one kills it.. either on success or error.

Solution would be to wait for the loops to finish (adding code after the loops brackets) and putting the success related code there. If an error is detected inside the loops, then the script never gets that far.

在此处输入图像描述

I have no idea, how you are actually linking the images to service, but I tried to clean up your code and make the order correct. I also did my best at explaining why and where. Hopefully, you understand the problem better from this or even better, find better options to optimise your code:

// TESTING: Lets see what is inside post values:
echo '<b>$_POST values</b><pre>'; print_r($_POST); echo '</pre>';

// TESTING: Lets see what is inside the files values:
echo '<b>$_FILES values</b><pre>'; print_r($_FILES); echo '</pre>';

// Above is for testing only..

// Probably better place to load important configs:
require 'config.php';

// Since these are the conditions for uploads, then they are global:
// no need for them to be inside the loop:
$allowed = array('jpg', 'png', 'jpeg');

// Maximum allowed filesize:
$max_allowed_file_size = 15000000; // which is 15mb


// We detect the submit buttons trigger name:
if (isset($_POST['add-service'])) {
    
    // Do the escape thingy:
    // NOTE: You should be using some mysqli class for database handling:
    $shop_name = mysqli_real_escape_string($conn, $_POST['shop_name']);
    $service_cat = mysqli_real_escape_string($conn, $_POST['service_cat']);
    $service_name = mysqli_real_escape_string($conn, $_POST['service_name']);
    $service_desc = mysqli_real_escape_string($conn, $_POST['service_desc']);
    $service_price = mysqli_real_escape_string($conn, $_POST['service_price']);
    $service_type = mysqli_real_escape_string($conn, $_POST['service_type']);
    $service_images = $_FILES['service_images'];
    
    // Lets deal with the errors before going forward with the rest of the script:
    // You don't need elseif here, because your callback is to redirect and exit anyways..
    if (empty($shop_name) || empty($service_cat) || empty($service_name) || empty($service_desc) || empty($service_price) || empty($service_type)) {
        header('Location: ../services.php?error=emptyFields');
        exit();
    }
    
    if (!preg_match('/^[a-zA-Z0-9]*$/', $shop_name) && !preg_match('/^[a-zA-Z0-9\s]*$/', $service_name) && !preg_match('/^[a-zA-Z0-9\s \. \-]*$/', $service_desc) && !preg_match('/^[0-9\.]*$/', $service_price) && !preg_match('/^[a-zA-Z0-9\s \.]*$/', $service_type)) {
        header('Location: ../services.php?error=invalidInputs');
        exit();
    }
    
    if (!preg_match('/^[a-zA-Z0-9]*$/', $shop_name)) {
        header('Location: ../services.php?error=invalidShopName');
        exit();
    }
    
    if (!preg_match('/^[a-zA-Z0-9\s]*$/', $service_name)) {
        header('Location: ../services.php?error=invalidserviceName');
        exit();
    }
    
    if (!preg_match('/^[a-zA-Z0-9\s \. \-]*$/', $service_desc)) {
        header('Location: ../services.php?error=invalidDescription');
        exit();
    }
    
    if (!preg_match('/^[0-9\.]*$/', $service_price)) {
        header('Location: ../services.php?error=invalidPrice');
        exit();
    }
    
    if (!preg_match('/^[a-zA-Z0-9\s \.]*$/', $service_type)) {
        header('Location: ../services.php?error=invalidStyle');
        exit();
    }
    // Nothing happened above, so that means the form validation should be fine and we can go forward with the images:
    
    
    // So as in your script, we count the images:
    $a = count($_FILES['service_images']['name']);
    
    // Now we do a "numeric loop", not an array loop, which is foreach:
    for ($i = 0; $i < $a; $i++) {
        
        // Since we have the key as numeric now, we can do what you did before, but without the foreach loop:
        $file_name = $_FILES['service_images']['name'][$i];
        $file_type = $_FILES['service_images']['type'][$i];
        $file_tempName = $_FILES['service_images']['tmp_name'][$i];
        $file_error = $_FILES['service_images']['error'][$i];
        $file_size = $_FILES['service_images']['size'][$i];
        
        // Get the file extension:
        // NOTE: This is not good, as you should really check the mime type of the file, not the extension.
        $fileActualExt = strtolower(end(explode('.', $file_name)));
        
        // TESTING: We check print out the data to make sure, that all looks fine:
        echo 'File with the key: ' . $i .' -- $file_name: ' . $file_name . '; $file_type: ' . $file_type . '; $file_tempName: ' . $file_tempName . '; $file_error: ' . $file_error . '; $file_size: ' . $file_size . '<br>';
        
        // Instead of making the code ugly, lets deal with errors, by killing the script before
        // NOTE: This is not good approach, you should be using Exceptions:
        // https://www.php.net/manual/en/language.exceptions.php
        // Check if the file extension is NOT in the allowed array
        if (!in_array($fileActualExt, $allowed)) {
            
            // Redirect:
            header('Location: ../services.php?error=invalidImageType');
            
            // Kill the script:
            exit('invalidImageType');
        }
        
        // Check if the file had an error:
        if ($file_error) {
            
            // Redirect:
            header('Location: ../services.php?error=invalidImage');
            
            // Kill the script:
            exit('invalidImage');
        }
        
        // Check if the image bytes are BIGGER > then max allowed file size variable:
        if ($file_size > $max_allowed_file_size) {
            
            // Redirect:
            header('Location: ../services.php?error=invalidSize');
            
            // Kill the script:
            exit();
        }
        
        // At this stage, hopefully, there has not been any errors above and we can deal with file freely:
        
        // Make new file name:
        $newFileName = preg_replace('/\s+/', '', $service_name) . $i . '.' . $fileActualExt;
        
        // echo $newFileName . "<br>";
        
        // Set the new destination:
        $fileDestination = '../../services/' . $newFileName;
        
        // Lets move the file already.
        // NOTE: Make sure that you have some bash code from server side, that deletes outdated / old temp files, so they dont take space:
        move_uploaded_file($file_tempName = $_FILES["service_images"]["tmp_name"][$i], $fileDestination);
        
        // Insert the image to database:
        // NOTE: Im not sure about your specific code, but just this is there location for that:
        $sql_images = "INSERT INTO service_images (shop_name, service_name) VALUES ('$shop_name', '$service_name')";
        $result = mysqli_query($conn, $sql_images);
            
        // PROBLEM: This is where you originally had the success message redirect and exit.
        // This means, you KILL the script and there for the loop.
        // But you have to understand, that you have two images or more, so the loop has to continue freely,
        // and you can do this sort of stuff at after the loop!
        //
        // header("Location: ../services.php?success");
        // exit();
        
    }
    // If nothing happened above, then the image uploads went trough nicely and we can deal with success messages or adding the service itself:
    
    // I have not used mysqli stmpt before, so I have no idea what is going on in this area..:
    // .. but this the locatin to deal with the services as this is the parent and the children are above.
    $sql = "INSERT INTO services (shop_name, service_cat, service_name, service_desc, service_price, service_type) VALUES (?,?,?,?,?,?)";
    $stmt = mysqli_stmt_init($conn);
    
    // I don't think you need this at all, but whatever:
    // Shouldnt this be above 
    if (!mysqli_stmt_prepare($stmt, $sql)) {
        
        // Redirect:
        header("Location: ../services.php?error=SaveError");
        
        // Kill the script:
        exit();
    }
    
    // This is adding the service I assume, it has to be outside the loop, as single submit = single service. But images are multiple.
    mysqli_stmt_bind_param($stmt, 'ssssss', $shop_name, $service_cat, $service_name, $service_desc, $service_price, $service_type);
    mysqli_stmt_execute($stmt);

    // This is where you can have the success redirect and exit, as this is after the loop:
    header("Location: ../services.php?success");
    exit();
}

NOTES:

  1. You should be using Exceptions for your error handling.
  2. Learn the difference between foreach and for loops.
  3. File extensions can be tricked, check out the file mime type instead
  4. Allowed file types array inside the loop is not very smart, as you will use it it more than once in all the loop cycles. Best to keep it at the top of the script, so its easier to setup in the future. Same goes for the filesize variable.
  5. It would make alot more sense to detect the file types, sizes via javascript before they even get to your server. This way you save temp file folder space issues and bandwidth basically.
  6. I don't understand where you actually use $result from the mysql. Or where do you link the images from service_images table to the actual service.
  7. Use <input type="file" name="service_images[]" multiple accept=".jpg, .png, .jpeg"> (the multiple accept=".jpg, .png, .jpeg" ) in the form to not allow the user to pick any other extensions. You can also use "images" value for all images.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM