Commits

weicongju committed 024a844

example

Comments (0)

Files changed (14)

 django
 local_settings.py
 setuptools*
-testdb.sqlite
+*.sqlite
+media/*

examples/s3project/settings.py

     'django.contrib.sites',
 )
 
-DEFAULT_FILE_STORAGE = 'backends.S3Storage.S3Storage'
+DEFAULT_FILE_STORAGE = 'storages.backends.s3.S3Storage'
 
 from S3 import CallingFormat
 AWS_CALLING_FORMAT = CallingFormat.SUBDOMAIN
     'Expires': 'Thu, 15 Apr 2010 20:00:00 GMT', # see http://developer.yahoo.com/performance/rules.html#expires
     'Cache-Control': 'max-age=86400',
     }
+AWS_STORAGE_BUCKET_NAME = 'test'
 
 # local_settings.py can be used to override environment-specific settings
 # like database and email that differ between development and production.

ossproject/cjapp/__init__.py

Empty file added.

ossproject/cjapp/models.py

+from django.db import models
+from django.forms import ModelForm
+from storages.backends.s3boto import S3BotoStorage
+
+class MyModel ( models.Model ):
+	file1 = models.FileField ( upload_to = 'media' )#, storage=S3BotoStorage )
+
+class MyModelForm ( ModelForm ):
+	class Meta:
+		model = MyModel

ossproject/cjapp/templates/cjapp/upload.html

+<form action="/upload" method="post" enctype="multipart/form-data">
+{{form}}
+<button>ok</button>
+</form>

ossproject/cjapp/tests.py

+"""
+This file demonstrates writing tests using the unittest module. These will pass
+when you run "manage.py test".
+
+Replace this with more appropriate tests for your application.
+"""
+
+from django.test import TestCase
+from django.test.client import Client
+
+
+class SimpleTest(TestCase):
+    def test_basic_addition(self):
+        c = Client ( )
+        print open ( "/Users/mini/oss" )
+        response = c.post ( "/upload/", { "file1" : "1", "file1" : open ( "/Users/mini/oss" ) } )
+        self.assertEqual ( 200, response.status_code)

ossproject/cjapp/views.py

+from django.http import HttpResponse
+from models import MyModelForm
+from django.shortcuts import render
+from pdb import set_trace as bp
+
+def upload ( request ):
+    if request.method == 'POST': # If the form has been submitted...
+    	# bn ()
+    	print request.POST
+        form = MyModelForm(request.POST, request.FILES) # A form bound to the POST data
+        if form.is_valid(): # All validation rules pass
+        	form.save ( )
+             # Redirect after POST
+        return HttpResponse ( "OK" )
+    else:
+        form = MyModelForm() # An unbound form
+	
+	return render ( request, 'cjapp/upload.html', { 'form': form })

ossproject/db.sqlite

Binary file added.

ossproject/manage.py

+#!/usr/bin/env python
+import os
+import sys
+
+if __name__ == "__main__":
+    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ossproject.settings")
+
+    from django.core.management import execute_from_command_line
+
+    execute_from_command_line(sys.argv)

ossproject/ossproject/__init__.py

Empty file added.

ossproject/ossproject/settings.py

+# Django settings for ossproject project.
+import os
+import sys
+
+ROOT = os.path.dirname ( __file__ )
+
+sys.path.insert ( 0, os.path.join ( ROOT, '../../' ) )
+
+DEBUG = True
+TEMPLATE_DEBUG = DEBUG
+
+ADMINS = (
+    # ('Your Name', 'your_email@example.com'),
+)
+
+MANAGERS = ADMINS
+
+DATABASES = {
+    'default': {
+        'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
+        'NAME': 'db.sqlite',                      # Or path to database file if using sqlite3.
+        'USER': '',                      # Not used with sqlite3.
+        'PASSWORD': '',                  # Not used with sqlite3.
+        'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
+        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
+    }
+}
+
+# Local time zone for this installation. Choices can be found here:
+# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
+# although not all choices may be available on all operating systems.
+# In a Windows environment this must be set to your system time zone.
+TIME_ZONE = 'America/Chicago'
+
+# Language code for this installation. All choices can be found here:
+# http://www.i18nguy.com/unicode/language-identifiers.html
+LANGUAGE_CODE = 'en-us'
+
+SITE_ID = 1
+
+# If you set this to False, Django will make some optimizations so as not
+# to load the internationalization machinery.
+USE_I18N = True
+
+# If you set this to False, Django will not format dates, numbers and
+# calendars according to the current locale.
+USE_L10N = True
+
+# If you set this to False, Django will not use timezone-aware datetimes.
+USE_TZ = True
+
+# Absolute filesystem path to the directory that will hold user-uploaded files.
+# Example: "/home/media/media.lawrence.com/media/"
+MEDIA_ROOT = os.path.join ( ROOT, "media" )
+
+# URL that handles the media served from MEDIA_ROOT. Make sure to use a
+# trailing slash.
+# Examples: "http://media.lawrence.com/media/", "http://example.com/media/"
+MEDIA_URL = ''
+
+# Absolute path to the directory static files should be collected to.
+# Don't put anything in this directory yourself; store your static files
+# in apps' "static/" subdirectories and in STATICFILES_DIRS.
+# Example: "/home/media/media.lawrence.com/static/"
+STATIC_ROOT = ''
+
+# URL prefix for static files.
+# Example: "http://media.lawrence.com/static/"
+STATIC_URL = '/static/'
+
+# Additional locations of static files
+STATICFILES_DIRS = (
+    # Put strings here, like "/home/html/static" or "C:/www/django/static".
+    # Always use forward slashes, even on Windows.
+    # Don't forget to use absolute paths, not relative paths.
+)
+
+# List of finder classes that know how to find static files in
+# various locations.
+STATICFILES_FINDERS = (
+    'django.contrib.staticfiles.finders.FileSystemFinder',
+    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
+#    'django.contrib.staticfiles.finders.DefaultStorageFinder',
+)
+
+# Make this unique, and don't share it with anybody.
+SECRET_KEY = 'l6f#4(k^=e^re0apqzcjj2ldq9nppm07ooxsaxqo*62yj4-1#a'
+
+# List of callables that know how to import templates from various sources.
+TEMPLATE_LOADERS = (
+    'django.template.loaders.filesystem.Loader',
+    'django.template.loaders.app_directories.Loader',
+#     'django.template.loaders.eggs.Loader',
+)
+
+MIDDLEWARE_CLASSES = (
+    'django.middleware.common.CommonMiddleware',
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    # 'django.middleware.csrf.CsrfViewMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'django.contrib.messages.middleware.MessageMiddleware',
+    # Uncomment the next line for simple clickjacking protection:
+    # 'django.middleware.clickjacking.XFrameOptionsMiddleware',
+)
+
+ROOT_URLCONF = 'ossproject.urls'
+
+# Python dotted path to the WSGI application used by Django's runserver.
+WSGI_APPLICATION = 'ossproject.wsgi.application'
+
+TEMPLATE_DIRS = (
+    # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
+    # Always use forward slashes, even on Windows.
+    # Don't forget to use absolute paths, not relative paths.
+    os.path.join ( ROOT, 'templates' )
+)
+
+INSTALLED_APPS = (
+    'storages',
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',
+    'django.contrib.sites',
+    'django.contrib.messages',
+    'django.contrib.staticfiles',
+    # Uncomment the next line to enable the admin:
+    # 'django.contrib.admin',
+    # Uncomment the next line to enable admin documentation:
+    # 'django.contrib.admindocs',
+    'cjapp'
+)
+
+# A sample logging configuration. The only tangible logging
+# performed by this configuration is to send an email to
+# the site admins on every HTTP 500 error when DEBUG=False.
+# See http://docs.djangoproject.com/en/dev/topics/logging for
+# more details on how to customize your logging configuration.
+LOGGING = {
+    'version': 1,
+    'disable_existing_loggers': False,
+    'filters': {
+        'require_debug_false': {
+            '()': 'django.utils.log.RequireDebugFalse'
+        }
+    },
+    'handlers': {
+        'mail_admins': {
+            'level': 'ERROR',
+            'filters': ['require_debug_false'],
+            'class': 'django.utils.log.AdminEmailHandler'
+        }
+    },
+    'loggers': {
+        'django.request': {
+            'handlers': ['mail_admins'],
+            'level': 'ERROR',
+            'propagate': True,
+        },
+    }
+}
+
+
+# AWS_HEADERS = {
+#     'Expires': 'Thu, 15 Apr 2010 20:00:00 GMT', # see http://developer.yahoo.com/performance/rules.html#expires
+#     'Cache-ContDEFAULTrol': 'max-age=86400',
+#     }
+DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage'
+AWS_STORAGE_BUCKET_NAME = 'cjdefault'
+AWS_S3_ACCESS_KEY_ID = 'AKIAIRFW2EC2IMNLF4LA'
+AWS_S3_SECRET_ACCESS_KEY = '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'

ossproject/ossproject/urls.py

+from django.conf.urls import patterns, include, url
+
+# Uncomment the next two lines to enable the admin:
+# from django.contrib import admin
+# admin.autodiscover()
+
+urlpatterns = patterns('',
+    # Examples:
+    # url(r'^$', 'ossproject.views.home', name='home'),
+    # url(r'^ossproject/', include('ossproject.foo.urls')),
+
+    # Uncomment the admin/doc line below to enable admin documentation:
+    # url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
+
+    # Uncomment the next line to enable the admin:
+    # url(r'^admin/', include(admin.site.urls)),
+    url ( r'upload', 'cjapp.views.upload', name='upload' )
+)

ossproject/ossproject/wsgi.py

+"""
+WSGI config for ossproject project.
+
+This module contains the WSGI application used by Django's development server
+and any production WSGI deployments. It should expose a module-level variable
+named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover
+this application via the ``WSGI_APPLICATION`` setting.
+
+Usually you will have the standard Django WSGI application here, but it also
+might make sense to replace the whole Django WSGI application with a custom one
+that later delegates to the Django one. For example, you could introduce WSGI
+middleware here, or combine a Django application with an application of another
+framework.
+
+"""
+import os
+
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ossproject.settings")
+
+# This application object is used by any WSGI server configured to use this
+# file. This includes Django's development server, if the WSGI_APPLICATION
+# setting points here.
+from django.core.wsgi import get_wsgi_application
+application = get_wsgi_application()
+
+# Apply WSGI middleware here.
+# from helloworld.wsgi import HelloWorldApplication
+# application = HelloWorldApplication(application)

storages/backends/oss.py

+import os
+import mimetypes
+import warnings
+
+try:
+    from cStringIO import StringIO
+except ImportError:
+    from StringIO import StringIO
+
+from django.conf import settings
+from django.core.files.base import File
+from django.core.files.storage import Storage
+from django.core.exceptions import ImproperlyConfigured
+
+try:
+    from oss.oss_api import OssAPI
+    # from S3 import AWSAuthConnection, QueryStringAuthGenerator, CallingFormat
+except ImportError:
+    raise ImproperlyConfigured("Could not load amazon's S3 bindings.\nSee "
+        "http://developer.amazonwebservices.com/connect/entry.jspa?externalID=134")
+
+ACCESS_KEY_NAME     = getattr(settings, 'AWS_S3_ACCESS_KEY_ID', '' )
+SECRET_KEY_NAME     = getattr(settings, 'AWS_S3_SECRET_ACCESS_KEY', '')
+# HEADERS             = getattr(settings, 'AWS_HEADERS', {})
+# DEFAULT_ACL         = getattr(settings, 'AWS_DEFAULT_ACL', 'public-read')
+# QUERYSTRING_ACTIVE  = getattr(settings, 'AWS_QUERYSTRING_ACTIVE', False)
+# QUERYSTRING_EXPIRE  = getattr(settings, 'AWS_QUERYSTRING_EXPIRE', 60)
+# SECURE_URLS         = getattr(settings, 'AWS_S3_SECURE_URLS', False)
+# BUCKET_PREFIX       = getattr(settings, 'AWS_BUCKET_PREFIX', '')
+# CALLING_FORMAT      = getattr(settings, 'AWS_CALLING_FORMAT', CallingFormat.PATH)
+# PRELOAD_METADATA    = getattr(settings, 'AWS_PRELOAD_METADATA', False)
+
+IS_GZIPPED          = getattr(settings, 'AWS_IS_GZIPPED', False)
+GZIP_CONTENT_TYPES  = getattr(settings, 'GZIP_CONTENT_TYPES', (
+    'text/css',
+    'application/javascript',
+    'application/x-javascript'
+))
+
+if IS_GZIPPED:
+    from gzip import GzipFile
+
+class OSSStorage(Storage):
+    """Amazon Simple Storage Service"""
+
+    def __init__(self, bucket=settings.AWS_STORAGE_BUCKET_NAME,
+            access_key=None, secret_key=None, acl=DEFAULT_ACL,
+            calling_format=CALLING_FORMAT, encrypt=False,
+            gzip=IS_GZIPPED, gzip_content_types=GZIP_CONTENT_TYPES,
+            preload_metadata=PRELOAD_METADATA):
+        warnings.warn(
+            "The s3 backend is deprecated and will be removed in version 1.2. "
+            "Use the s3boto backend instead.",
+            PendingDeprecationWarning
+        )
+        self.bucket = bucket
+        self.acl = acl
+        self.encrypt = encrypt
+        self.gzip = gzip
+        self.gzip_content_types = gzip_content_types
+        self.preload_metadata = preload_metadata
+
+        if encrypt:
+            try:
+                import ezPyCrypto
+            except ImportError:
+                raise ImproperlyConfigured("Could not load ezPyCrypto.\nSee "
+                    "http://www.freenet.org.nz/ezPyCrypto/ to install it.")
+            self.crypto_key = ezPyCrypto.key
+
+        if not access_key and not secret_key:
+            access_key, secret_key = self._get_access_keys()
+
+        self.connection = AWSAuthConnection(access_key, secret_key,
+                            calling_format=calling_format)
+        self.generator = QueryStringAuthGenerator(access_key, secret_key,
+                            calling_format=calling_format,
+                            is_secure=SECURE_URLS)
+        self.generator.set_expires_in(QUERYSTRING_EXPIRE)
+
+        self.headers = HEADERS
+        self._entries = {}
+
+    def _get_access_keys(self):
+        access_key = ACCESS_KEY_NAME
+        secret_key = SECRET_KEY_NAME
+        if (access_key or secret_key) and (not access_key or not secret_key):
+            access_key = os.environ.get(ACCESS_KEY_NAME)
+            secret_key = os.environ.get(SECRET_KEY_NAME)
+
+        if access_key and secret_key:
+            # Both were provided, so use them
+            return access_key, secret_key
+
+        return None, None
+
+    @property
+    def entries(self):
+        if self.preload_metadata and not self._entries:
+            self._entries = dict((entry.key, entry)
+                                for entry in self.connection.list_bucket(self.bucket).entries)
+        return self._entries
+
+    def _get_connection(self):
+        return AWSAuthConnection(*self._get_access_keys())
+
+    def _clean_name(self, name):
+        # Useful for windows' paths
+        return os.path.join(BUCKET_PREFIX, os.path.normpath(name).replace('\\', '/'))
+
+    def _compress_string(self, s):
+        """Gzip a given string."""
+        zbuf = StringIO()
+        zfile = GzipFile(mode='wb', compresslevel=6, fileobj=zbuf)
+        zfile.write(s)
+        zfile.close()
+        return zbuf.getvalue()
+
+    def _put_file(self, name, content):
+        if self.encrypt:
+
+            # Create a key object
+            key = self.crypto_key()
+
+            # Read in a public key
+            fd = open(settings.CRYPTO_KEYS_PUBLIC, "rb")
+            public_key = fd.read()
+            fd.close()
+
+            # import this public key
+            key.importKey(public_key)
+
+            # Now encrypt some text against this public key
+            content = key.encString(content)
+
+        content_type = mimetypes.guess_type(name)[0] or "application/x-octet-stream"
+
+        if self.gzip and content_type in self.gzip_content_types:
+            content = self._compress_string(content)
+            self.headers.update({'Content-Encoding': 'gzip'})
+
+        self.headers.update({
+            'x-amz-acl': self.acl,
+            'Content-Type': content_type,
+            'Content-Length' : str(len(content)),
+        })
+        response = self.connection.put(self.bucket, name, content, self.headers)
+        if response.http_response.status not in (200, 206):
+            raise IOError("S3StorageError: %s" % response.message)
+
+    def _open(self, name, mode='rb'):
+        name = self._clean_name(name)
+        remote_file = S3StorageFile(name, self, mode=mode)
+        return remote_file
+
+    def _read(self, name, start_range=None, end_range=None):
+        name = self._clean_name(name)
+        if start_range is None:
+            headers = {}
+        else:
+            headers = {'Range': 'bytes=%s-%s' % (start_range, end_range)}
+        response = self.connection.get(self.bucket, name, headers)
+        if response.http_response.status not in (200, 206):
+            raise IOError("S3StorageError: %s" % response.message)
+        headers = response.http_response.msg
+
+        if self.encrypt:
+            # Read in a private key
+            fd = open(settings.CRYPTO_KEYS_PRIVATE, "rb")
+            private_key = fd.read()
+            fd.close()
+
+            # Create a key object, and auto-import private key
+            key = self.crypto_key(private_key)
+
+            # Decrypt this file
+            response.object.data = key.decString(response.object.data)
+
+        return response.object.data, headers.get('etag', None), headers.get('content-range', None)
+
+    def _save(self, name, content):
+        name = self._clean_name(name)
+        content.open()
+        if hasattr(content, 'chunks'):
+            content_str = ''.join(chunk for chunk in content.chunks())
+        else:
+            content_str = content.read()
+        self._put_file(name, content_str)
+        return name
+
+    def delete(self, name):
+        name = self._clean_name(name)
+        response = self.connection.delete(self.bucket, name)
+        if response.http_response.status != 204:
+            raise IOError("S3StorageError: %s" % response.message)
+
+    def exists(self, name):
+        name = self._clean_name(name)
+        if self.entries:
+            return name in self.entries
+        response = self.connection._make_request('HEAD', self.bucket, name)
+        return response.status == 200
+
+    def size(self, name):
+        name = self._clean_name(name)
+        if self.entries:
+            entry = self.entries.get(name)
+            if entry:
+                return entry.size
+            return 0
+        response = self.connection._make_request('HEAD', self.bucket, name)
+        content_length = response.getheader('Content-Length')
+        return content_length and int(content_length) or 0
+
+    def url(self, name):
+        name = self._clean_name(name)
+        if QUERYSTRING_ACTIVE:
+            return self.generator.generate_url('GET', self.bucket, name)
+        else:
+            return self.generator.make_bare_url(self.bucket, name)
+
+    def modified_time(self, name):
+        try:
+           from dateutil import parser, tz
+        except ImportError:
+            raise NotImplementedError()
+        name = self._clean_name(name)
+        if self.entries:
+            last_modified = self.entries.get(name).last_modified
+        else:
+            response = self.connection._make_request('HEAD', self.bucket, name)
+            last_modified = response.getheader('Last-Modified')
+        # convert to string to date
+        last_modified_date = parser.parse(last_modified)
+        # if the date has no timzone, assume UTC
+        if last_modified_date.tzinfo == None:
+            last_modified_date = last_modified_date.replace(tzinfo=tz.tzutc())
+        # convert date to local time w/o timezone
+        return last_modified_date.astimezone(tz.tzlocal()).replace(tzinfo=None)
+
+    ## UNCOMMENT BELOW IF NECESSARY
+    #def get_available_name(self, name):
+    #    """ Overwrite existing file with the same name. """
+    #    name = self._clean_name(name)
+    #    return name
+
+
+class PreloadingS3Storage(S3Storage):
+    pass
+
+class S3StorageFile(File):
+    def __init__(self, name, storage, mode):
+        self._name = name
+        self._storage = storage
+        self._mode = mode
+        self._is_dirty = False
+        self.file = StringIO()
+        self.start_range = 0
+
+    @property
+    def size(self):
+        if not hasattr(self, '_size'):
+            self._size = self._storage.size(self._name)
+        return self._size
+
+    def read(self, num_bytes=None):
+        if num_bytes is None:
+            args = []
+            self.start_range = 0
+        else:
+            args = [self.start_range, self.start_range+num_bytes-1]
+        data, etags, content_range = self._storage._read(self._name, *args)
+        if content_range is not None:
+            current_range, size = content_range.split(' ', 1)[1].split('/', 1)
+            start_range, end_range = current_range.split('-', 1)
+            self._size, self.start_range = int(size), int(end_range)+1
+        self.file = StringIO(data)
+        return self.file.getvalue()
+
+    def write(self, content):
+        if 'w' not in self._mode:
+            raise AttributeError("File was opened for read-only access.")
+        self.file = StringIO(content)
+        self._is_dirty = True
+
+    def close(self):
+        if self._is_dirty:
+            self._storage._put_file(self._name, self.file.getvalue())
+        self.file.close()