mirror of
https://github.com/wassname/flask-cloudy.git
synced 2026-08-20 12:20:28 +08:00
Flask-Cloudy 0.10.0
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
0.7.0
|
||||
0.10.0
|
||||
- Removed LOCAL_PATH configuration. Use CONTAINER as the LOCAL_PATH
|
||||
- Change config prefix to STORAGE_*
|
||||
|
||||
0.6.0
|
||||
- More pythonic
|
||||
|
||||
@@ -34,7 +34,7 @@ For local file storage, it also provides a flask endpoint to access the files.
|
||||
@app.route("/upload", methods=["POST", "GET"]):
|
||||
def upload():
|
||||
if request.method == "POST":
|
||||
file = request.file.get("file")
|
||||
file = request.files.get("file")
|
||||
my_upload = storage.upload(file)
|
||||
|
||||
# some useful properties
|
||||
@@ -185,7 +185,7 @@ Storage:
|
||||
|
||||
The **Storage** class allows you to access, upload, get an object from the Storage.
|
||||
|
||||
##### Storage(provider, key=None, secret=None, container=None)
|
||||
##### Storage(provider, key=None, secret=None, container=None, allowed_extensions=None)
|
||||
|
||||
- provider: the storage provider:
|
||||
|
||||
@@ -209,6 +209,8 @@ The **Storage** class allows you to access, upload, get an object from the Stora
|
||||
- For cloud storage, use the **BUCKET NAME**
|
||||
|
||||
- For LOCAL provider, it's the directory path where to access the files
|
||||
|
||||
- allowed_extensions: List of extensions to upload to upload
|
||||
|
||||
|
||||
##### Storage.init_app(app)
|
||||
@@ -239,7 +241,7 @@ It will also setup a server endpoint when STORAGE_PROVIDER == LOCAL
|
||||
@app.route("/upload", methods=["POST", "GET"]):
|
||||
def upload():
|
||||
if request.method == "POST":
|
||||
file = request.file.get("file")
|
||||
file = request.files.get("file")
|
||||
my_upload = storage.upload(file)
|
||||
|
||||
# some useful properties
|
||||
@@ -452,11 +454,15 @@ To save the object to a local path
|
||||
print(my_new_file) # Will print -> /my/new/path/my_object.txt
|
||||
|
||||
|
||||
##### Object.download(name=None)
|
||||
##### Object.download_url(timeout=60, name=None)
|
||||
|
||||
Return a URL that triggers the browser download of the file.
|
||||
|
||||
Use the url to download the file
|
||||
- timeout: int - The time in seconds to give access to the url
|
||||
|
||||
- name: str - for LOCAL only, to rename the file being downloaded
|
||||
|
||||
.
|
||||
|
||||
storage = Storage(provider, key, secret, container)
|
||||
my_object = storage.get("my_object.txt")
|
||||
@@ -475,12 +481,12 @@ Use the url to download the file
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
I hope you find this library useful, enjoy!
|
||||
|
||||
Thank you
|
||||
|
||||
Mardix :)
|
||||
|
||||
---
|
||||
|
||||
License: MIT - Copyright 2015 Mardix
|
||||
License: MIT - Copyright 2015 Mardix
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
__author__ = 'mardochee.macxis'
|
||||
@@ -0,0 +1,37 @@
|
||||
|
||||
from flask import Flask, request, render_template, redirect, abort, url_for
|
||||
from flask_cloudy import Storage
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
app.config.update({
|
||||
"STORAGE_PROVIDER": "LOCAL",
|
||||
"STORAGE_CONTAINER": "./data",
|
||||
"STORAGE_KEY": "",
|
||||
"STORAGE_SECRET": "",
|
||||
"STORAGE_SERVER": True
|
||||
})
|
||||
|
||||
storage = Storage()
|
||||
storage.init_app(app)
|
||||
|
||||
@app.route("/")
|
||||
def index():
|
||||
|
||||
return render_template("index.html", storage=storage)
|
||||
|
||||
@app.route("/view/<path:object_name>")
|
||||
def view(object_name):
|
||||
obj = storage.get(object_name)
|
||||
print obj.name
|
||||
return render_template("view.html", obj=obj)
|
||||
|
||||
@app.route("/upload", methods=["POST"])
|
||||
def upload():
|
||||
file = request.files.get("file")
|
||||
my_object = storage.upload(file)
|
||||
return redirect(url_for("view", object_name=my_object.name))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(debug=True, port=5000)
|
||||
@@ -0,0 +1,3 @@
|
||||
Hello World!
|
||||
|
||||
from flask_cloud import Storage
|
||||
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head lang="en">
|
||||
<meta charset="UTF-8">
|
||||
<title>Flask-Cloudy</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>Flask-Cloudy</h1>
|
||||
|
||||
|
||||
<form action="{{ url_for('upload') }}" method="post" enctype="multipart/form-data">
|
||||
Select image to upload:
|
||||
<input type="file" name="file" id="fileToUpload"> <br>
|
||||
<input type="submit" value="Upload File" name="submit">
|
||||
</form>
|
||||
|
||||
<hr>
|
||||
|
||||
<h3>List of files available on the storage:</h3>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<th>Name</th>
|
||||
<th>Size</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for obj in storage %}
|
||||
<tr>
|
||||
<td><a href="{{ url_for('view', object_name=obj.name) }}">{{ obj.name }}</a></td>
|
||||
<td>{{ obj.size }} bytes</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,23 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head lang="en">
|
||||
<meta charset="UTF-8">
|
||||
<title>Flask-Cloudy</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>Flask-Cloudy: View File</h1>
|
||||
<a href="{{ url_for('index') }}"><- Home</a>
|
||||
<br> <br>
|
||||
|
||||
Name: {{ obj.name }} <br><br>
|
||||
Size: {{ obj.size }} bytes <br><br>
|
||||
|
||||
Short url: {{ obj.short_url }} <br><br>
|
||||
|
||||
View file: <a href="{{ obj.url }}">{{ obj.url }}</a> <br><br>
|
||||
|
||||
{% set download_url = obj.download_url() %}
|
||||
Download: <a href="{{ download_url }}">{{ download_url }}</a> <br><br>
|
||||
</body>
|
||||
</html>
|
||||
+60
-23
@@ -3,6 +3,10 @@ Flask-Cloudy
|
||||
"""
|
||||
|
||||
import os
|
||||
import datetime
|
||||
import base64
|
||||
import hmac
|
||||
import hashlib
|
||||
import warnings
|
||||
from werkzeug.utils import secure_filename
|
||||
from werkzeug.datastructures import FileStorage
|
||||
@@ -13,9 +17,11 @@ from libcloud.storage.types import Provider, ObjectDoesNotExistError
|
||||
from libcloud.storage.providers import DRIVERS, get_driver
|
||||
from libcloud.storage.base import Object as BaseObject, StorageDriver
|
||||
from libcloud.storage.drivers import local
|
||||
from six.moves.urllib.parse import urlparse, urlunparse, urljoin
|
||||
from six.moves.urllib.parse import urlparse, urlunparse, urljoin, urlencode
|
||||
import slugify
|
||||
|
||||
|
||||
|
||||
SERVER_ENDPOINT = "FLASK_CLOUDY_SERVER"
|
||||
|
||||
EXTENSIONS = {
|
||||
@@ -126,7 +132,7 @@ class Storage(object):
|
||||
:param secret: str - provider secret
|
||||
:param container: str - the name of the container (bucket or a dir name if local)
|
||||
:param allowed_extensions: list - extensions allowed for upload
|
||||
:param app: Flask object -
|
||||
:param app: object - Flask instance
|
||||
:param kwargs: any other params will pass to the provider initialization
|
||||
:return:
|
||||
"""
|
||||
@@ -134,10 +140,10 @@ class Storage(object):
|
||||
if app:
|
||||
self.init_app(app)
|
||||
|
||||
if allowed_extensions:
|
||||
self.allowed_extensions = allowed_extensions
|
||||
|
||||
if provider:
|
||||
if allowed_extensions:
|
||||
self.allowed_extensions = allowed_extensions
|
||||
|
||||
kwparams = {
|
||||
"key": key,
|
||||
"secret": secret
|
||||
@@ -343,11 +349,15 @@ class Storage(object):
|
||||
obj = self.get(object_name)
|
||||
if obj:
|
||||
dl = request.args.get("dl")
|
||||
name = request.args.get("name")
|
||||
name = request.args.get("name", obj.name)
|
||||
|
||||
if get_file_extension(name) != obj.extension:
|
||||
name += ".%s" % obj.extension
|
||||
|
||||
_url = obj.get_cdn_url()
|
||||
return send_file(_url,
|
||||
as_attachment=True if dl else False,
|
||||
attachment_filename=name or False,
|
||||
attachment_filename=name,
|
||||
conditional=True)
|
||||
else:
|
||||
abort(404)
|
||||
@@ -369,7 +379,7 @@ class Object(object):
|
||||
container
|
||||
|
||||
@method
|
||||
download()
|
||||
download() use save_to() instead
|
||||
delete()
|
||||
"""
|
||||
|
||||
@@ -492,21 +502,6 @@ class Object(object):
|
||||
"""
|
||||
return "%s/%s" % (self.container.name, self.name)
|
||||
|
||||
def download(self, name=None, signed=True):
|
||||
"""
|
||||
Trigger a browse download
|
||||
:return:
|
||||
"""
|
||||
if "local" in self.driver.name.lower():
|
||||
return url_for(SERVER_ENDPOINT,
|
||||
object_name=self.path,
|
||||
dl=1,
|
||||
name=name)
|
||||
else:
|
||||
if signed:
|
||||
pass
|
||||
pass
|
||||
|
||||
def save_to(self, destination, name=None, overwrite=False, delete_on_failure=True):
|
||||
"""
|
||||
To save the object in a local path
|
||||
@@ -527,3 +522,45 @@ class Object(object):
|
||||
overwrite_existing=overwrite,
|
||||
delete_on_failure=delete_on_failure)
|
||||
return obj_path if file else None
|
||||
|
||||
def download_url(self, timeout=60, name=None):
|
||||
"""
|
||||
Trigger a browse download
|
||||
:param timeout: int - Time in seconds to expire the download
|
||||
:param name: str - for LOCAL only, to rename the file being downloaded
|
||||
:return: str
|
||||
"""
|
||||
if "local" in self.driver.name.lower():
|
||||
return url_for(SERVER_ENDPOINT,
|
||||
object_name=self.name,
|
||||
dl=1,
|
||||
name=name,
|
||||
_external=True)
|
||||
else:
|
||||
driver_name = self.driver.name.lower()
|
||||
expires = (datetime.datetime.now()
|
||||
+ datetime.timedelta(seconds=timeout)).strftime("%s")
|
||||
|
||||
if 's3' in driver_name or 'google' in driver_name:
|
||||
|
||||
s2s = "GET\n\n\n{expires}\n/{object_name}"\
|
||||
.format(expires=expires, object_name=self.path)
|
||||
h = hmac.new(self.driver.secret, s2s, hashlib.sha1)
|
||||
s = base64.encodestring(h.digest()).strip()
|
||||
_keyIdName = "AWSAccessKeyId" if "s3" in driver_name else "GoogleAccessId"
|
||||
params = {
|
||||
_keyIdName: self.driver.key,
|
||||
"Expires": expires,
|
||||
"Signature": s
|
||||
}
|
||||
urlkv = urlencode(params)
|
||||
return "%s?%s" % (self.secure_url, urlkv)
|
||||
|
||||
elif 'cloudfiles' in driver_name:
|
||||
return self.driver.ex_get_object_temp_url(self._obj,
|
||||
method="GET",
|
||||
timeout=expires)
|
||||
else:
|
||||
raise NotImplemented("This provider '%s' doesn't support or "
|
||||
"doesn't have a signed url "
|
||||
"implemented yet" % self.provider_name)
|
||||
|
||||
@@ -19,7 +19,7 @@ Supported storage:
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
__NAME__ = "Flask-Cloudy"
|
||||
__version__ = "0.7.0"
|
||||
__version__ = "0.10.0"
|
||||
__author__ = "Mardix"
|
||||
__license__ = "MIT"
|
||||
__copyright__ = "2015"
|
||||
|
||||
@@ -24,12 +24,6 @@ class App(object):
|
||||
STORAGE_ALLOWED_EXTENSIONS=[])
|
||||
|
||||
|
||||
def _setup_function():
|
||||
pass
|
||||
|
||||
def _teardown_function():
|
||||
pass
|
||||
|
||||
def test_get_file_extension():
|
||||
filename = "hello.jpg"
|
||||
assert get_file_extension(filename) == "jpg"
|
||||
@@ -170,3 +164,6 @@ def test_save_to():
|
||||
file2 = o.save_to(CWD + "/data", name="my_new_file", overwrite=True)
|
||||
assert os.path.isfile(file)
|
||||
assert file2 == CWD + "/data/my_new_file.txt"
|
||||
|
||||
|
||||
from flask_cloudy import Storage
|
||||
Reference in New Issue
Block a user