views:

308

answers:

4

I'm creating an online game in PHP where users can create playable characters. Each character can have a user-uploaded portrait. A player can simultaneously have multiple characters, and the pictures for them can be changed anytime. Naturally, the pictures have to be resized and re-compressed to avoid huge files. Here's my problem:

When the player changes his data (among it the picture), and then hits "save", server side validation kicks in. It checks for things like non-unique character names, empty mandatory fields, etc. If any errors are found, they are displayed. In this case the form should be pre-populated with the data the player entered, so he only has to change the faulty bit, not re-type everything. But how do you save the picture in such a "temporary" state?

I cannot pre-populate the file upload field, the browsers don't allow that. If I save it in a temporary file, the picture then has to be cleaned up at some point, because the player can simply close his browser and abort the whole process. When should that be? And what file name should I choose for the temporary file? If the player opens the same character to edit in two browser tabs, they should not conflict (each of them should have their own copy).

How would you solve this problem?

+6  A: 

I'd save the file to a temporary location and store both a unique identifier as well as the current timestamp in the filename. Then put the filename in the user's session. When a user has successfully created or updated their account, you save the image file to its permanent location and remove the temporary file. You can run a cron process to scan the temporary directory and check the timestamps, deleting any files older than your expiration (an hour perhaps).

If you're unable to run a cron job, you could always just launch the directory clean-up each time you have a successful create/update validation. This might be a bit more inefficient (extra directory reads and possibly file operations for every successful submission) but unless you're dealing with a lot of traffic, you probably won't even notice.

pix0r
Nice answer. Upvoted. However, weird as it might sound, my client doesn't like cron jobs, because he hasn't had any dealings with them and doesn't want new untested things. Maybe you can come up with another solution without cron jobs?
Vilx-
Sure, just incorporate the directory clean-up into the image upload handling code. When the create/update validation is finished (successfully), and you delete the temporary image file, scan the rest of the directory to look for expired images and delete those.
pix0r
Well... it's an option. Actually, I already settled for storing it in session instead of a temporary folder (I'm storing sessions in MySQL). Since the resized image will be pretty small, it's no big problem, I think.
Vilx-
If the performance suffers however, I'll remember this.
Vilx-
A: 

Create a table to hold references to images.

When a file is uploaded, if it's a valid image, do all your resizing, etc, and create a record in the table that points at the file.

Pass the id of the reference record around with the form data. Display the image when you redisplay the form, so the user knows they don't have to re-upload.

When you finally accept the new character object, set avatar_id or whatever.

Run a regular cron-job to cull orphaned image records (deleting the files on disk as well).

timdev
A: 

You could always populate a disabled text box to hold the name of the picture - it won't populate the browse input field, but is better than nothing. For editing, to avoid conflicts you could create some a "modifing" column for each user's characters, and on a character editing request change the value to true. When the user saves the character, set it back to false. For each edit request, grant it only when the "modifing" is false.

Robert DeBoer
It would get pretty awkward when the user would simply close the tab with an edit form open. The lock wouldn't be lifted, and he couldn't edit anything else.
Vilx-
A: 

I'd recommend either updating the image immediately, regardless of error in the form, or separating the image updating to a separate form. That way you'll get rid of two problems without complex state machines and cleaning up.

nikc
At the expense of making the UI more annoying. :(
Vilx-
True, perhaps, but I'd still pursue this, as making things more complicated than they have to be, is just asking for trouble. Just my 2c.
nikc