Create folder based on ID for every new product

Ok so currently i have a working image upload functionality but to make it easier to read etc i want to have it so that when you create a new product its creates a folder based on the id for that product and then places the images inside that product.

Here is my current controller code:

    public function add()
    {
        if($_SERVER['REQUEST_METHOD']=='POST'){
            // Sanitize POST Array
            $_POST = filter_input_array(INPUT_POST,FILTER_SANITIZE_STRING);

            $data = [
                'title' => trim($_POST['title']),
                'content' => trim($_POST['content']),
				'image' => $_FILES['image']['name'],
				'image_dir' => $_FILES['image']['tmp_name'],
				'image_size' => $_FILES['image']['size'],
                'title_err' => '',
                'content_err' => '',
                'image_err' => ''
            ];
            
            					// Upload directory
            		///	mkdir("the product id");
					$upload_dir = FULL_ROOT . "/uploads/blog/";
					// For checking if image already exists 
					$target_file = $upload_dir . basename($data['image']);
					// Get file extension
					$imgExt = strtolower(pathinfo($data['image'],PATHINFO_EXTENSION));
					// Valid extensions
					$valid_extensions = array('jpeg', 'jpg', 'png', 'gif');
					// Validate data
					if(empty($data['image'])){
						$data['image_err'] = "Please upload image";
					} elseif (!in_array($imgExt, $valid_extensions)) {
						$data['image_err'] = "Please upload valid image (jpeg, jpg, png, gif)";
					} elseif (file_exists($target_file)){
						$data['image_err'] = "Image already exists";
					} elseif($_FILES['image']['error'] == 2){
						// UPLOAD_ERR_FORM_SIZE 
						// Value: 2; The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.
						// <input type="hidden" name="MAX_FILE_SIZE" value="300000">
						$data['image_err'] = "Image file too large";
					} elseif ($data['image_size'] > 300000){
						// In case user is geeky enough to bypass max image file size in client side
						// Limit image size to 300KB
						$data['image_err'] = "Image file too large";
					}

            // Validate
            if( empty($data['title']) ){
                $data['title_err'] = 'Please enter the title';
            }
            if( empty($data['content']) ){
                $data['content_err'] = 'Please enter the body';
            }

            // Make sure no errors
            if (empty($data['image_err']) && empty($data['title_err']) && empty($data['content_err']) ){
                // Validated
                if( $this->blog->addPost($data) ){
                    //flash('post_message', 'Post Added');
                    move_uploaded_file($data['image_dir'], $upload_dir . $data['image']);
                    redirect('admin');
                } else{
                    die('Something went wrong');
                }
            } else {
            // Load the view
             $this->view('admin/add', $data);
          }

       } else{
          $data = [
             'title' => '',
             'content' => '',
             'image' => ''
          ];
          $this->view('admin/add', $data);
       }
    }

So currently what happens is it places the image inside the uploads/blog folder but how do i go about doing it so that say for example there is nothing in the database and the first id = 1 it creates a folder inside the uploads/blog/ called 1 and then inside it places the preview image?

Thanks

Is the id value based on the id assigned when you call addPost()? If it is, then between calling that function and moving the uploaded file, you need to recover the id value, create the new folder and move the file there instead of the default folder. I don’t know the framework you’re using so can’t suggest how you would get the id value that you need.

its an add function so as its auto-increment it does the ID automatically which is where i am stuck! I don’t know how to get the id as i have not created it yet etc.

Thanks for the quick reply!

If this is for a single image, I wouldn’t necessarily do it the way you are. Since it’s a single image, we can safely assume that storing just the filename would suffice. So how you would do that is store filename at the same time you are inserting data. The way you are trying to go about it takes too much time and effort. Why create more folders when all you really want to do is upload an image? Store it in the same folder and just generate a new filename. Takes less time and effort. And when you want to display the image, all you do is grab the ID of the post or article and there you’ll have an image column. Then you just specify that image column and you are set for life.

Though I would like to say that your image checker does absolutely nothing. The type index of the posted file refers back the file type of the file extension.

EDIT: Actually, I just noticed you aren’t using type index for your image checker. You only reference the file extension, this is even worse than what I have described below.

This is actually a bad thing because people can just change malicous_file.txt to malicious_file.jpg and it’ll go through. What you need to be checking is the mime content type of the temp file. This holds the true value of the file because the temp file gets moved to a temporary location. From here, the user cannot spoof the extension since temporary files don’t have extensions. This is a much safer approach than what you currently have.

1 Like

If it’s an auto-increment, then it will be created when the product is inserted into the table. The database connection can give you the last-insert-id, if you have direct access to it. Did you write the addPost() function, or is it a third-party product you’ve bought or downloaded?

But yes, as @spaceshiptrooper said, having an individual folder each containing a single image doesn’t sound like a great idea.

1 Like

Sorry at the moment it’s for a single image but I’m eventually gonna be coding it to have 2 images and a file inside it which is why I wanna do the folder!

Thanks for letting me know about the type bit. I will recode that bit but can u please explain how I would do the folder bit because it will hold more than just a single image and am unsure how to do it so that it creates the folder with the correct ID

Thanks

Create test db and table

-- Dumping database structure for test
CREATE DATABASE IF NOT EXISTS `test` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `test`;

-- Dumping structure for table test.person
CREATE TABLE IF NOT EXISTS `person` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `firstname` varchar(100) NOT NULL,
  `lastname` varchar(100) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8;

PHP http://php.net/manual/en/function.mkdir.php

$path = __DIR__;    // Path where you want to create new folders
$firstName = 'Dave';
$lastName = 'Developer';

// Connect to db and insert person
$pdo = new PDO('mysql:host=localhost;dbname=test', 'dbuser', 'dbpass');
$sql = 'insert into person (firstname, lastname) values(:firstname, :lastname)';
$stmt = $pdo->prepare($sql);
$stmt->bindParam(':firstname', $firstName);
$stmt->bindParam(':lastname', $lastName);
$stmt->execute();

// Create folder which is named with the last inserted person id
mkdir($path .DIRECTORY_SEPARATOR. $pdo->lastInsertId());

I still wouldn’t do it the way you are trying to do. It’s inefficient and takes up too much execution time. If a post or article requires more than 1 image to be uploaded, you can still do it the way I am suggesting. Insert the image file names along with the data and fetch those images based on post IDs or article IDs.

How would you recommend i do it then? I’m trying to eventually have it like CodeCanyon so as you can see in this example: https://s3.envato.com/files/243146558/newidentity1.jpg it has the folder and its the id of the product?

Thanks

Ok. Well for one, I would probably separate these things in different tables. For instance, since we know we are uploading an image, we can pretty much create a separate table for our image sets. So for instance our image table would have something like

id | post_id | filename | timestamp

Then each row would be something like

1, 3, 'jfjnwnk.jpg', '0000-00-00 00:00:00 A.M.'

Assuming the filename will be generated randomly and uniquely so that each file aren’t being over written if one of the same filename already exists.

Next, just insert your data into a different table. If you want to fetch the image name, just use the post or article’s ID to get the right one from the image table.

True, but that is not the whole story. The problem with mime checking is that just checks the value of certain bits in the file and then makes a conclusion. That means that a clever hacker can make an .exe file look like a .pdf as far as a mime checker is concerned. So what you need to do is detect the mime type and then append the extension that goes with that mime type to the filename (if it’s not already there) so the .exe will become malicious.exe.pdf and it will be treated as a pdf, never as an .exe file.

3 Likes

I think Facebook does this too. I’ve tried first creating a txt file holding the mime type of text/plain. Then changed the file to a jpg extension and uploaded the file, it rejected the image and said to upload a real image. So either they are checking for mime type or they are doing something similar.

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.