views:

60

answers:

4

I am working currently on a web project where users can create image galleries and upload pictures. Optionally they can mark pictures as private so that nobody else can look at them. Now i am not sure how to properly implement the protection mechanism. Of course i have some ideas but none of them seems to be optimal.

Here is one of my ideas:

Create a table for user images:

image_key (PK) | user_id | public_image (boolean)

the picture will be saved on the harddisk using the iamge_key and can be accessed via http by an url looking like this:

http://www.myCompany.com/images/image_key

a servlet will be mapped to the url path images, the key will be extracted, a stream to the file on the harddisk will be openend and the picutre will be streamed. additionally there will be a reverse proxy in front of the servlet container to provide some caching.

The issue with this solution is that my serlvet would have to go to the database and check if the image with the given key is public or not.

My question: Can this be done without hitting the database? (some fancy ideas) Can someone provide a better solution to store and keep track of the pictures? How would a solution look like where besides public and private pictures also some pictures are shared to firends only?


Please note that this question is not about storing pictures in a database or somewhere else but concerns access rights management of binary resources in a web application environment.

A: 

To avoid having to go to the database so often, how about the following URL patterns:

1. http://www.mycompany.com/images/<user_id>/public/<image_key>        
2. http://www.mycompany.com/images/<user_id>/private/<image_key>        
3. http://www.mycompany.com/images/<user_id>/shared/<image_key>

For 1, obviously no DB lookup is required - the image can be served to anybody immediately. For 2, your servlet would have to check that the ID of the active user matches the user_id in the request - again, hopefully no DB lookup required, just a check of a session variable.

The only case in which a DB call would be needed is 3, to check the relationship between the requesting user and the user who owns the image.

Of course, you'll need to be very careful about caching to ensure that your cache doesn't serve up private or shared images to unauthorised users...

dogbert
This has the major caveat that the links get broken whenever the user decides to change the visibility of the image. Also, this doesn't prevent an user from just changing the path in the link, which would still require you to do a DB hit.
BalusC
@BalusC - yes, agree about the risk of links getting broken if visibility changes, and with your answer - DB lookups needn't be expensive, so perhaps best not to 'prematurely optimise'. However, I don't quite follow the second part of your comment around users changing paths in links. As I see it, public/private images would be straightforward - access is based on a session variable. The only time you'd need to go back to the DB would be if the new path is to a shared image. Is that it, or could you possibly elaborate for my understanding?
dogbert
If another user edits a `/private/imagekey` link to `/public/imagekey` and fires a request on it, what would (should) happen? Right, a DB lookup :) I did't said that you should avoid DB lookups to all extent btw. It was the OP who would like to minimize it.
BalusC
+1  A: 

If the DB table is properly indexed and you're using a connection pool, then hitting the DB is cheap. I would just keep it as is. You can at highest have a copy of the table in a Map in the application scope. But this may eat too much server memory. If you're using an ORM framework like JPA/Hibernate, you could also just turn the second level cache on to delegate the caching to the ORM. It will generally do its job very well.

As to the client side caching, you'd like to have a short expire time (1 hour?) and provide a doHead() in the servlet which in turn does basically just the same as doGet() but then without writing any bit to the response body. You would also like to check for If-None-Match and If-Last-Modified headers in the servlet if the client supplied them. You can find here a basic example of a servlet which does that.

BalusC
+1  A: 

My question: Can this be done without hitting the database? (some fancy ideas)

Yup, you can do it without hitting the database. We've got something similar and just wanted to put something quick in place.

The user is marking the resource private or public when he's uploading it.

We do something very simple:

  • public resources have a "tinyurl like" URL, say: abcd.org/aZ3tD (part of the point of the very short tinyurl-link thing is so that people who want to cut/paste/twitter it don't have to use an additional layer of bit.ly or tinyurl)

  • private resources aren't meant to be shared nor archived, so users don't care about a URL looking like: abcd.org/private/da499c3314e2fdce6a10a8b985489671971c187d

The first part of that URL is the user's ID.

So only the user da499c3314e2 (which must be logged in) can access resource fdce6a10a8b985489671971c187d

You asked for some way to do this without hitting the DB, this should work...

NoozNooz42
so if a user wants to change a public pic into a private pic the file on the harddisk has to be renamed? did i get that right?
ManBugra
@ManBugra: the pic in our DB in our case has its real name. It's just the URL that does some different mapping magic depending on whether the pic is public or private. If he changes the pic's status (public/private), you *must* anyway change something in the DB: the public/private status. Then depending on the status the pic is accessible using either the "simple" or "complicated" URL.
NoozNooz42