utils.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. import datetime
  2. import email
  3. import mimetypes
  4. import os
  5. import time
  6. import boto.exception
  7. from boto.s3.connection import S3Connection
  8. from django.conf import settings
  9. from django.core.cache import get_cache
  10. GZIP_CONTENT_TYPES = (
  11. 'text/css',
  12. 'application/javascript',
  13. 'application/x-javascript',
  14. 'text/javascript'
  15. )
  16. class ConfigMissingError(Exception):
  17. """Raise this when (AWS) settings are missing."""
  18. pass
  19. def get_bucket_and_key(name):
  20. """Connect to S3 and grab bucket and key."""
  21. key, secret, host = get_aws_info()
  22. conn = S3Connection(key, secret, host=host)
  23. try:
  24. bucket = conn.get_bucket(name)
  25. except boto.exception.S3ResponseError:
  26. bucket = conn.create_bucket(name)
  27. return bucket, boto.s3.key.Key(bucket)
  28. def get_aws_info():
  29. if not hasattr(settings, 'AWS_ACCESS_KEY_ID') or \
  30. not hasattr(settings, 'AWS_SECRET_ACCESS_KEY'):
  31. raise ConfigMissingError
  32. host = getattr(settings, 'AWS_S3_HOST', 's3.amazonaws.com')
  33. key, secret = settings.AWS_ACCESS_KEY_ID, settings.AWS_SECRET_ACCESS_KEY
  34. return key, secret, host
  35. def get_pending_key():
  36. return getattr(settings, 'BUCKET_UPLOADS_PENDING_KEY', 's3-pending')
  37. def get_pending_delete_key():
  38. return getattr(settings, 'BUCKET_UPLOADS_PENDING_DELETE_KEY',
  39. 's3-pending-delete')
  40. def get_s3sync_cache():
  41. return get_cache(getattr(settings, 'BUCKET_UPLOADS_CACHE_ALIAS',
  42. 'default'))
  43. def guess_mimetype(f):
  44. return mimetypes.guess_type(f)[0]
  45. def upload_file_to_s3(file_key, filename, key, do_gzip=False,
  46. do_expires=False, verbosity=0):
  47. """Details about params:
  48. * file_key is the relative path from media, e.g. media/folder/file.png
  49. * filename is the full path to the file, e.g.
  50. /var/www/site/media/folder/file.png
  51. """
  52. headers = {}
  53. content_type = guess_mimetype(filename)
  54. file_obj = open(filename, 'rb')
  55. if content_type:
  56. headers['Content-Type'] = content_type
  57. file_size = os.fstat(file_obj.fileno()).st_size
  58. filedata = file_obj.read()
  59. if do_gzip:
  60. # Gzipping only if file is large enough (>1K is recommended)
  61. # and only if file is a common text type (not a binary file)
  62. if file_size > 1024 and content_type in GZIP_CONTENT_TYPES:
  63. filedata = compress_string(filedata)
  64. headers['Content-Encoding'] = 'gzip'
  65. gzip_file_size = len(filedata)
  66. if verbosity > 1:
  67. print ("\tgzipped: %dk to %dk" % \
  68. (file_size / 1024, gzip_file_size / 1024))
  69. file_size = gzip_file_size
  70. headers['Content-Length'] = str(file_size)
  71. if do_expires:
  72. # HTTP/1.0
  73. headers['Expires'] = '%s GMT' % (email.Utils.formatdate(
  74. time.mktime((datetime.datetime.now() +
  75. datetime.timedelta(days=365 * 2)).timetuple())))
  76. # HTTP/1.1
  77. headers['Cache-Control'] = 'max-age %d' % (3600 * 24 * 365 * 2)
  78. try:
  79. key.name = file_key
  80. key.set_contents_from_string(filedata, headers, replace=True)
  81. key.set_acl('public-read')
  82. finally:
  83. file_obj.close()
  84. def compress_string(s):
  85. """Gzip a given string."""
  86. import gzip
  87. try:
  88. from cStringIO import StringIO
  89. except ImportError:
  90. from StringIO import StringIO
  91. zbuf = StringIO()
  92. zfile = gzip.GzipFile(mode='wb', compresslevel=6, fileobj=zbuf)
  93. zfile.write(s)
  94. zfile.close()
  95. return zbuf.getvalue()