I've been trying to work out the 'best practices' way to manage file uploads with Turbogears 2 and have thus far not really found any examples. I've figured out a way to actually upload the file, but I'm not sure how reliable it us.
Also, what would be a good way to get the uploaded files name?
file = request.POST['file']
permanent_file = open(os.path.join(asset_dirname,
file.filename.lstrip(os.sep)), 'w')
shutil.copyfileobj(file.file, permanent_file)
file.file.close()
this_file = self.request.params["file"].filename
permanent_file.close()
So assuming I'm understanding correctly, would something like this avoid the core 'naming' problem? id = UUID.
file = request.POST['file']
permanent_file = open(os.path.join(asset_dirname,
id.lstrip(os.sep)), 'w')
shutil.copyfileobj(file.file, permanent_file)
file.file.close()
this_file = file.filename
permanent_file.close()
I just want anyone who comes here looking for answers, to know that Allesandro Molina's great library Depot constitutes the best answer to this question.
It solves both the naming and copying issues, and it will incorporate nicely into your TurboGears application. You can use it with MongoDB GridFS, as in this example:
from depot.manager import DepotManager
# Configure a *default* depot to store files on MongoDB GridFS
DepotManager.configure('default', {
'depot.backend': 'depot.io.gridfs.GridFSStorage',
'depot.mongouri': 'mongodb://localhost/db'
})
depot = DepotManager.get()
# Save the file and get the fileid
fileid = depot.create(open('/tmp/file.png'))
# Get the file back
stored_file = depot.get(fileid)
print stored_file.filename
print stored_file.content_type
or you can easily create attachment fields in your SQLAlchemy models, like:
from depot.fields.sqlalchemy import UploadedFileField
class Document(Base):
__tablename__ = 'document'
uid = Column(Integer, autoincrement=True, primary_key=True)
name = Column(Unicode(16), unique=True)
content = Column(UploadedFileField)
… and then, storing documents with attached files (the source can be a file or bytes) becomes as easy as:
doc = Document(name=u'Foo', content=open('/tmp/document.xls'))
DBSession.add(doc)
Depot supports both LocalFileStorage
, MongoDB's GridFSStorage
, and Amazon's S3Storage
. And, at least for files stored locally and in S3, the fileid
will be generated by uuid.uuid1()
.
@mhawke - you're right you have to handle that - depends on what you are doing with the file, if it doesn't matter if there is a name collision eg you only care for the latest version of some data then theres probably no issue, or if the filename isn't actually important just the file contents, but its still bad practice.
You could use a named tempfile in a tmp dir, then move the file once validated to its final location. Or you could check the filename doesn't already exist like so:
file.name = slugify(myfile.filename)
name, ext = os.path.splitext(file.name)
while os.path.exists(os.path.join(permanent_store, file.name)):
name += '_'
file.name = name + ext
raw_file = os.path.join(permanent_store, file.name)
The slugify method would be used to tidy up the filename...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With