Most efficient way to name a user avatar? - php

I want to let my users upload an avatar on their profile. My first idea was to name the avatar file like this: [user_id].jpg. So even if a user updates its avatar, it keeps the same name.
The problem with that is that if I use caching on the server (or even if it's used on the client) the new avatar won't show up.
My new solution is to name the file like this:
[user_id]_[random_number].jpg
and store the random number in the Users table. How would you generate this number in the most efficient way? Or maybe there is a better solution?

You should be able to invalidate the cache when the user uploads a new avatar.
If this is not possible you could just store it as [uid]_[YYYYMMDDhhmmss].jpg or something. No need to generate anything random...

I would do something like:
$avatarName = $userId . uniqid();
// add extension if needed, store it
It will be fast and do what you want. uniqid()
EDIT
As suggested by other users, you should drop the userId from the image name. Having a public userId may lead to problems in the future.
Also, uniqid() alone should work.
$avatarName = uniqid();
// add extension if needed, store it

Have you thought about configuring ETags in your .htaccess?
See:
http://developer.yahoo.com/blogs/ydn/posts/2007/07/high_performanc_11/
Though you can change filenames, you will need to manage the cleanup and pointing operations (remove/rename the old file, tell your app the new file). If you are happy to do this, you can simply append the users id with the unix timestamp at the point of upload, its unlikely they will be able to upload the same file to the same second. If you want to make it even more unique, append a random number/uniquid.

With a random number there is a miniscule (negligible) chance of a collision, but why not start at 1 and just increase the number each time since you are storing this number.

Related

How to store user's profile picture in mysql

I am developing a chat application for iOS.
In this app, the users can set up an image as their profile picture.
So my question is, how can i be able to store images in mysql ?
I have seen that many people say, just store the link to the image(on device) in mysql, but how will the images be available on different iOS Devices, from a database right ?
I have also tried using BLOB, but when the table rows are displayed(json encoded), the value for BLOB field comes out to be NULL.
Please answer in brief.
Thanks,
This is fairly simple :) The physical image will be stored on a server and you will store in your DB only the image name, or the relative path to the image, or however you want it. So, if you like, you will have to store in your DB a "pointer" to that image.
So:
- Image in folder on the server
- In DB -> path/to/file or file_name.format OR if you know you path, and you know your format just file_name
Hope this helps! :D
May be late But I just found way to do that.
Way 1: Get image from user and rename with the id (Pref. Primary key) and in Your public_html create folder for images. Set proper permissions for that folder. and save image in that folder and the url of that image will be stored DB column . Like
https://www.example.com/images/user1.png do in that style.
Way 2: use other things to rename that image like user's email,username etc
way 3: In above 2 cases it may happen that other users,hackers may try to download images by using IDs,emails etc.
That's why , another way you can do is you can generate a hash for profile and check if the hash already exists in column if exits then generate another one. Likely I don't think there is possibility of generating similar hashes.but you may check to avoid error in future. and now rename image using that generated hash.
You may also reduce size of that link column using only storing generated hash and in your app declare some variables and achieve
https://www.example.com/images/generated_hash.png
here the url will be same in all columns excluding that hash key.
you may only store that hashes.
Hope it will help
Reference from : https://www.techupdates.live/how-to-save-profile-picture-in-php-in-5-simple-steps/

converting md5 hashes in urls to be shorter shorter

Im working on a image hosting website, and to prevent "already exists" error, i md5 the images that are being uploaded, the problem is that the URL to that website is already quite long plus the whole MD5 hash makes it even longer, is there any way to make the URL shorter?
It's not necessary to use the md5 string as your image filename. To ensure the uniqueness of the images, you can try the following solution:
md5() every new image a user uploads
Store the md5() value in a database
Next time a user uploads an image, check if the item already exists in your database
If exists, prevent user from uploading the image. Else, proceed.
Repeat
you can keep a id to the hash value mapping with you on the image hosting server. You can store this mapping in redis or mysql as both are persistant databases.
you can use name of image and time when uploaded to make it unique but shorter. use like this
$img_name = $uploaded_name.time().$file_ext;
Hence the name will be shorter but unique.
Just use the unix timestamp to ensure a new and unique file name all the time and keep is length shorter as well.

Best way to store large amount of data of users

I store files of users in their own name directory something like
/username/file01.jpg
/username/file02.mp4
/username/file03.mp3
But if more users come and upload more files then this creates problem because this will lead to migration of some or many users to another drive.I choose username directory solution first because i dont want filenames to be mixed. I dont want to change filename too. Also if another user upload same filename then it creates problem ,if the files are stored with original name.
What could be the best way to do this. I have one solution but want to ask community is this the best way .
i will use sequential folders and then hash the file name to some thing very unique and store into the directory.
What i will do is store the original name of file and username into database and hashvalue of filename which is stored in Disk.
When anyone want to access that file,I will read that file through php either replace the name or will do something at that point so that the file is downloaded as original filename.
I have only this proposed solution in mind. Do you guys have any other better than this one.
Edit:
I use folder system too, and possibly for 2nd way i will use virtual folders.
My database is MongoDB
Guys all your answers were awesome and really helpful. i wanted to give bounty to everyone, thats why i left it so that community can provide automatically.
Thanks all for your answers.I really appreciate it.
Could you create relational MySQL tables? e.g.:
A users table and a files table.
Your users table would keep track of everything you are (I assume) already tracking:
id, name, email, etc.
Then the files table would store something like:
id, fileExtension, fileSize, userID <---- userID would be the foreign key pointing to the id field in the files table.
then when you save your file you could save it as it's id.fileExtension and use a query to pull the user associated with that file, or all files associated with a user.
e.g.:
SELECT users.name, files.id, files.extension
FROM `users`
INNER JOIN `files` on users.id = files.userID;
I handle file metadata on the database and retrive the files with a UUID. What i do is:
Content based identification
MD5 from file's content
Namespaced UUID:v5 to generate unique identifier based on user's uuid and file's md5.
Custom function to generate path based on 'realname'.
Save on the database: uuid, originalname (the uploaded name), realname (the generated name), filesize, and mime. (optional dateAdded, and md5)
File retrival.
UUID to retrive metadata.
regenerate filepath based on realname.
Originalname is used to show a familiar name to the user that downloads the file.
I process the file's name assigning it a namespaced UUID as the database primary key, and Generate the path based on User and filename. The precondition is that your user has a uuid assigned to him. The following code will help you avoid id collisions on the database, and help you identify files by its contents (If you ever need to have a way to spot duplicate content and not necesarily filenames).
$fileInfo = pathinfo($_FILE['file']['name']);
$extension = (isset($fileInfo['extension']))?".".$fileInfo['extension']:"";
$md5Name = md5_file($_FILE['file']['tmp_name']); //you could use other hash algorithms if you are so inclined.
$realName = UUID::v5($user->uuid, $md5Name) . $extension; //UUID::v5(namespace, value).
I use a function to generate the filepath based on some custom parameteres, you could use $username and $realname. This is helpful if you implement a distributed folder structure which you might have partitioned on file naming scheme, or any custom scheme.
function generateBasePath($realname, $customArgsArray){
//Process Args as your requirements.
//might as well be "$FirstThreeCharsFromRealname/"
//or a checksum that helps you decide which drive/volume/mountpoint to use.
//like some files on the local disk and some other from an Amazon::S3 mountpoint.
return $mountpoint.'/'.$generatedPath;
}
As an added bonus this also:
helps you maintain a versioned file repository if you add an attribute on the file's record of which file (uuid) it has replaced.
create a application Access Control List if you add an attributes of 'owner' and/or 'group'
also works on a single folder structure.
Note: I used php's $_FILE as an example of the file source based on this question's tags. It can be from any file source or generated content.
Since you already use MongoDB, I would suggest checking out GridFS. It's a specification that allows you to store files(even if they are larger than 16mb) into MongoDB collections.
It is scalable, so you'll have no problems if you add another server, it also stores metadata, it is possible to read files in chunks and it also has built in backup functions.
Another tactic is to create a 2-dimensional structure where the first level of directories are the first 2 characters of the username, then the second level is the remaining characters (similar to how Git stores its SHA-1 object IDs). For example:
/files/jr/andomuser/456.jpg
for user 'jrandomuser'.
Please note that as usernames will likely not be distributed as randomly as SHA-1 values, you may need to add another level later on. Doubt it, though.
I would generate a GUID based on a hash of the filename, Date and Time of the Upload and username for the Filename, save those values, as well as the path to the file in a database for later use. If you generate such a GUID, the filenames can not be guessed.
As example lets take user Daniel Steiner (me) uploads a file called resume.doc on the 23rd of april 2013 at 37 past twelve am to your server. this would give a base value of
Daniel_Steiner+2013/23/04+00:37+resume.doc which then would be as MD5 hash 05c2d2f501e738b930885d991d136f1e. to ensure that the file will be opened in the right programm, we will afterwards add the right file ending and thus will get something like http://link.to/your/site/05c2d2f501e738b930885d991d136f1e.doc If your useraccounts already have a user id, you could add those to the URL, for example, if my User ID would be 123145, the url would be http://link.to/your/site/123145/05c2d2f501e738b930885d991d136f1e.doc
If you save the original filename to the database, you can later also offer a downloadscript that provides the file with its original filename for download, even tough it has another filename on your server.
In case you can use symbolic links, relocating the files on another harddisk shouldn't be a problem either.
If you want to, I could come up with an PHP example as well - shouldn't be too much code.
Since filesystem is a tree, not a graph (faceted classification), its hard to come up with some way for it to easily represent multiple entities, like users, media types, dates, events, image crop types etc. Thats why using relational database is easier - it is convertible to graph.
But since its another level of abstraction, you need to write functions that do low-level synchronization yourself, including avoiding name collisions, long path names, large file count per folder, ease of transfer per-entity, horizontal scaling etc. So it depends how complex your application needs to be
I suggest to use following database structure:
Where File table has at least:
IDFile is an auto_increment column / primary key.
UserID is nullable foreign key.
For FK_File_User I suggest:
ON UPDATE NO ACTION -- IDUser is auto_increment too. No changes need to be tracked.
ON DELETE SET NULL -- If user deleted, then File is not owned. Might be deleted
-- with CRON job or something else.
Still, another columns might be added to the File table:
Actual upload date and time
Actual mime-type
Actual storage place (for distributed storage systems)
Download count (another table might be a better solution)
etc...
Some benefits:
You don't need to calculate file size, hash, extension or any file meta, because you might obtain it with one database operation.
You can obtain statistics for each user of a file count / space used / whatever you wrote to File table by single SELECT ... GROUP BY ... WITH ROLLUP statement, and it would be faster, than analysis of actual files, which may be spread across multiple storage devices.
You may apply file access permissions for different users. It will cost not significant change of table structures database.
I don't consider as an option, that original filenames needed at storage, because of two reasons:
File may have name, which not correctly supported by Server OS filesystem, like Cyrillic ones.
Two different files may have completely identical names, so one of them might be overwritten by another.
So, there is a solution:
1) Rename files when they are uploaded to IDFile from INSERT into File table. It's safe and there are no dublicates.
2) Restore name of the file, when it's needed / downloaded, like:
// peform query to "File" table by given ID
list($name, $ext, $size, $md5) = $result->fetch_row();
$result->free();
header('Content-Length: ' . $size);
header('Content-MD5: ' . $md5);
header('Accept-Ranges: bytes');
header('Connection: close');
header('Content-Type: application/force-download');
header('Content-Disposition: attachment; filename="' . $name . '.' . $ext . '"');
// flush file content
3) Actual files may be stored within single directory (because IDFile is safe) and IDUser-named subdirectory - depends on a situation.
4) As IDFile is a direct sequence, if some of files are gone missing, you may obtain their database meta by evaluating missing segments of actual filenames sequence. Then, you may "inform owners", "delete file meta" or both of this actions.
I'm against the idea of storing large actual files in DBMS itself as a binary content.
DBMS is about data and analysis, it's not a FileSystem, and should never be used in that way, if my humble opinion matters.
You can install a LDAP server. LDAP lookup is very fast since it is highly optimized for heavy read operations. You can even query for data
LDAP organizes the data in a tree like fashion.
You can organize data as following example "user->IP address->folder->file name". This way file could be physically/geographically spread out and you can fetch the location very quickly.
You can query too using standard LDAP query for e.g. get all the list of file for a particular user or get the list of files in the folder etc.
Mongodb to store the actual filename (eg: myImage.jpg) and other attributes (eg: MIME types), plus $random-text.jpg from 2. & 3. below
Generate some $random-text, eg: base_convert(mt_rand(), 10, 36) or uniqid($username, true);
Physically store the file as $random-text.jpg - always good to maintain same extension
NOTE: Use filter_var() to ensure the input filename doesn't pose security risk to Mongodb.
Amazon S3 is reliable and cheap, be aware of "Eventual Concurrency" with S3.
Assuming users have a unique ID (Primary Key) in the database, if a user with ID 73 uploads a file, save it like this:
"uploads/$userid_$filename.$ext"
For example, 73_resume.doc, 73_myphoto.jpg
Now, when fetching files, use this code:
foreach (glob("uploads/$userid_*.*") as $filename) {
echo $filename;
}
This can be combined with hashing solutions (stored in the DB), so that a user who gets a download path as 73_photo.jpg does not randomly try 74_photo.jpg in the browser address bar.

Generating a time-based unique file name for uploads without creating a race condition

I'm generating a unique filename for uploaded files with the following code
$date = date( 'U' );
$user = $_SERVER[REMOTE_ADDR];
$filename = md5($date.$user);
The problem is that I want to use this filename again later on in the script, but if the script takes a second to run, I'm going to get a different filename the second time I try to use this variable.
For instance, I'm using an upload/resize/save image upload script. The first operation of the script is to copy and save the resized image, which I use a date function to assign a unique name to. Then the script processses the save and saves the whole upload, and assigns it a name. At the end of the script ($thumb and $full are the variables), I need to insert into a MySQL database, the filenames I used when i saved the uploads.
Problem is, sometimes on large images it takes more than a second (or during the process, the seconds change) resulting in a different filename being put into the database than is what the file is actually saved under.
Is this just not a good idea to use this method of naming?
AFAIK it's a great way to name the files, although I would check file_exists() and maybe tack on a random number.
You need to store that filename in a variable and reference it again later, instead of relying on the algorithm each time. This could be stored in the user $_SESSION, a cookie, a GET variable, etc between pageloads.
Hope that helps
Just want add that php has a function to create identifiers: uniqid. You can also prefix the identifier with a string (date maybe?).
Always validate your user's input, and the server headers!
I would recommend storing the file name in the session (as per AI). If you store it in one of the other variables, it is more likely for the end user to be able to attack the system through it. MD5 of user concatenated with rand() would be a nice way to get a long list of unique values. Just using rand() would probably have a higher percentage of conflicts.
I am not sure about the process that you are following for uploading files, but another way to handle file uploads is with PHP's built in handlers. You can upload the file and then use the "secure" methods for pulling uploaded files out of the temporary space. (the temporary space in this instance can be safely located outside of the open base dir directive to prevent tampering). is_uploaded_file() and move_uploaded_file() from: http://php.net/manual/en/features.file-upload.post-method.php example 2 might handle the problem you are encountering.
Definitely check for an existing file in that location if you are choosing a filename on the fly. If user input is allowed in any way shape or form, validate and filter the argument to make sure it is safe. Also, if the storage folder is web accessible, make sure you munge the name and probably the extension as well. You do not want someone to be able to upload code and then be able to execute it. That officially leads to BAD activities.
I just discovered that PHP has a built-in function for this, called tempnam. It even avoids race conditions. See http://php.net/manual/en/function.tempnam.php.
Why not to use
$filename = md5(rand());
This will be pretty much unique in every case. And if you find that $filename already exists you can just call it again.
Not a good idea using ID dependent on time – if you upload two images at the same time, the later one can overwrite the earlier. You should look at function such as uniqid(). However, if this upload/resize/save script is meant to be "single-user", then this is not such a big problem.
To the problem itself. If I were you, I would just save the computed filename to some variable a use the variable from that point. Computing already computed is waste of time. And when uploading some really big images, or more images at once, script can take even 20 seconds. You cannot depend on fact that you'll make everything you want in one second.

iPhone Uploading unique images to a server running PHP / MYSQL

I'm working on an iPhone app that will upload images to a web server. I was wondering if anyone had any tips on how to generate unique names for each image file that gets uploaded.
I'm sure there are a million ways to do this, but if anyone has any suggestions I'd really appreciate it! Thanks.
you could create a GUID and save the image as that name ..
Thanks
The simplest solution (assuming you're storing these in a database) is to have an auto-increment field in the database, and use that to rename the file as it's uploaded. That way you'll end up with image00000001.jpg, image00000002.jpg, etc.
The simplest would be to convert the current time into a string and use that as a name. will always be unique :)
Or if you have a private key in your database, use it with a generic string to generate a unique name for each image.
You could use the unix timestamp. This would allow you to update a record with a new file while still keeping the same id, instead of having to create a new record each time a file is changed. Some like:
$uploadData = pathinfo($_FILES['file']['tmp_name']);
move_uploaded_file($_FILES['file']['tmp_name'], time() . '.' . $uploadData['extension']);
I'd recommend a lot more checking to ensure the file is what you are looking for, such as mime type/extension checking, max size, and ensure the file is an uploaded file using is_uploaded_file().
You may want to consider using the device id of the generating phone to insure uniqueness across the universe of iPhones.

Categories