tags:

views:

112

answers:

5

I'm trying to add user submitted articles to my website, (only for admins). With each article comes an option to upload up to 3 images. My database is set up like this

Articles

id
user_id
title
body
date_added
last_edited

Photos

id (auto_increment)
article_id

First I save the article in the database, then I upload the photo (temporarily) then I create a new photo record in the database saving the article_id. Then I rename the uploaded photo to be the same as the primary key of the photo record, and to be a png.

$filename = $photo->id . '.png';

I figured this would be a good way to prevent files form being overwritten. This seems flawed to me. Any suggestions on how I should save my records and photos?

Thanks

+1  A: 

Hash them.

I've used PHP's MD5 function to hash images as they were uploaded to a gallery. It has two major benefits: different images will not overwrite each other, identical images will. That means each unique picture will have a unique name. Accept the upload, hash the stored data, build the filename (hash.extension), check for an existing file. If one exists, delete the temporary file and just use the existing copy. Otherwise, rename the temporary file and keep it. You may need to keep track of what is linking to each file (to make sure you don't delete a file in use somewhere else), but it can save a lot of space on duplicates and is an easy way to get unique names.

Also, you can't just rename the files to use the PNG extension, you need to convert them to PNG format or save the original extension. Using the wrong extension may cause problems in some places, some browsers, and is just generally incorrect.

peachykeen
This is definitely worth considering. There are cases where it may be convenient to have multiple copies of the same file (so that one user's delete doesn't delete for all) and other cases where the space-saving nature of defining unique by the actual file contents could be very useful.
Devin Ceartas
+1  A: 

For what it's worth, here's a method I include in my file uploading classes to make sure i don't overwrite existing files. I pass the upload file name ( $_FILES[$img_field]["name"] ) and the directory to save the file into as parameters, so that this remains a general method.

 private function unique_file_upload($filename, $dir) {
      $filename = preg_replace("/[^.\w]/",'_',$filename);
      $filename = preg_replace("/__+/",'_',$filename);
      preg_match("/^(.*)\.(\w{2,4})$/",$filename,$f);
      if ( file_exists( $dir . $filename ) ) {
          $num = 1;
          while ( file_exists( $dir . $filename ) ) {
              preg_match("/^(.*)\.(\w{2,4})$/",$filename,$f);
              preg_match("/(\d+)$/",$f[1],$n);
              if ($n[1]) {
                  $x = $n[1];
                  $num = $n[1] + 1;
                  $num .= '.';
                  $filename = preg_replace("/$x\./",$num,$filename);  
              }
              else {
                  $filename = $f[1] . $num . '.' . $f[2];
              }
          }
      }
      return $filename;
  }
Devin Ceartas
+1  A: 

There's nothing really wrong with just using the database primary key to name the file.

The only real downside I see is that you end up with fairly useless filenames. Someone might complain down the road that more descriptive names would help with search engine rankings, etc.

As another answer points out, renaming a file to end in .png will not make it a png. You might want to take a look at PHP's built-in GD library (or look up ImageMagick) if you want to actually convert image formats.

timdev
True words there.
Ben
+1  A: 

If the image uses an auto-increment primary key as a name there's absolutely no chance of having two images with the same name.

Hashing or doing anything extra to assure uniqueness in the name is worthless. Even more, it would imply adding an extra image name field in the DB.

The only way images could get overwrited is if you reset the DB auto-increment counter, and in order to do that you need to delete previous records on the table or the counter would get recalculated again by the DBMS to highest id record on the table.

In other words, using your algorithm, you won't have two images with the same name.

Ben
A: 

The proper way is to store the files with the associated record's primary key ID. To handle the "but then it's just a useless number" you can store the original filename in the "photos" table. If you're allowing multiple image types, you can also store the mime type, as well.

You then use a simple script that'd retrieve the file's name from the database and serve it to the client with a Content-disposition: attachment; filename=$filename header. This way, even though the filename on your server is (say) "3527", the client will see a download of "originalimagefilename.jpg" or whatever.

Marc B