1. Preliminary preparation
After installing the Pymongo with PIP or Easy_install, you can mongodb through Python.
Then install a flask to be used as a Web server.
Of course, MONGO also have to install. For Ubuntu users, especially those using Server 12.04, the latest version of the installation is a bit of a struggle, specifically
sudo apt-key adv--keyserver hkp://keyserver.ubuntu.com:80--recv 7f0ceb10
Echo ' Deb http:// Downloads-distro.mongodb.org/repo/ubuntu-upstart Dist 10gen ' | sudo tee/etc/apt/sources.list.d/mongodb.list
sudo apt-get update
sudo apt-get install Mongodb-10gen
If you feel the same way as I do, let's identify what files the user uploaded by uploading the suffix of the file name it's all about the yam. When the cucumber as the same deceive themselves, then it is best to prepare a Pillow library
Copy Code code as follows:
or (more appropriate for Windows users)
Copy Code code as follows:
2, Positive
2.1 Flask File Upload
Flask on the Internet that example is actually divided into two sections so that people do not spit. Let's get the simplest one out of here first, no matter what the file.
Import flask
app = flask. Flask (__name__)
app.debug = True
@app. Route ('/upload ', methods=[' POST ')
def upload ():
f = flask.request.files[' uploaded_file ']
print f.read () return Flask.redirect ('
/')
@app. Route ('/')
def index (): Return
' '
<!doctype html>
Note: In the upload function, use Flask.request.files[key] to get the uploaded file object, the KEY is the name value of the input in the page form
Because it is in the background output content, so the test is best to take a plain text file to measure.
2.2 Save to MongoDB
If not, the most rapid and basic storage scenario requires only
Import Pymongo
import bson.binary from
Cstringio import stringio
app = flask. Flask (__name__)
app.debug = True
db = Pymongo. Mongoclient (' localhost ', 27017). Test
def save_file (f):
content = Stringio (F.read ())
Db.files.save (dict (
content= bson.binary.Binary (Content.getvalue ()))
@app. Route ('/upload ', methods=[' POST '))
def upload ():
f = flask.request.files[' uploaded_file ']
save_file (f) return
flask.redirect ('/' )
The content into a Bson.binary.Binary object, and then throw it into the mongodb can be.
Now try uploading a file and you can see it in the MONGO shell through Db.files.find ().
But content is almost invisible to the naked eye, and MONGO is displayed as BASE64 encoding even in plain text files.
2.3 Providing file access
Given the ID of the file that is stored in the database (as part of the URI), returns the contents of the file to the browser, as follows
Def save_file (f):
content = Stringio (F.read ())
C = dict (Content=bson.binary.binary (Content.getvalue ()))
Db.files.save (c) return
c[' _id ']
@app. Route ('/f/<fid> ')
def serve_file (FID):
f = Db.files.find_one (Bson.objectid.ObjectId (FID)) return
f[' content ']
@app. Route ('/upload ', methods=[' POST ') ]
def upload ():
f = flask.request.files[' uploaded_file ']
FID = Save_file (f)
return Flask.redirect ('/f/' + str (FID))
After uploading the file, the upload function jumps to the corresponding file browsing page. As a result, the contents of the text file can be previewed correctly, if not so picky line breaks and consecutive spaces are eaten by the browser.
2.4 When the file is not found
There are two cases, one, the database ID format is not correct, then Pymongo will throw abnormal bson.errors.InvalidId; Second, the object (!) cannot be found, at which point Pymongo returns None.
It's a simple way to deal with it.
@app. Route ('/f/<fid> ')
def serve_file (FID):
import bson.errors
try:
f = db.files.find_one ( Bson.objectid.ObjectId (FID))
if F is None:
raise Bson.errors.InvalidId () return
f[' content ']
except Bson.errors.InvalidId:
flask.abort (404)
2.5 correct MIME
From now on to upload the file strictly checks, text files, dogs and scissors can not upload.
Judge the picture file before saying we use Pillow
From PIL import Image
allow_formats = set ([' JPEG ', ' png ', ' gif '])
def save_file (f):
content = Stringio ( F.read ())
try:
mime = image.open (content). Format.lower ()
if mime not in Allow_formats:
raise IOError ()
except IOError:
flask.abort (=)
c = dict (Content=bson.binary.binary (Content.getvalue ()) )
Db.files.save (c) return
c[' _id ']
Then try uploading the text file must be virtual, the file can be transmitted to normal. No, also not normal, because after the jump, the server did not give the correct mimetype, so still preview the text of the way to preview a lump of binary garbled.
To solve this problem, we have to put MIME into the database; Also, the mimetype is transmitted correctly when the file is given.
Def save_file (f):
content = Stringio (F.read ())
try:
mime = image.open (content). Format.lower ()
if MIME Not in Allow_formats:
raise IOError ()
except IOError:
flask.abort (=)
c = dict (content= Bson.binary.Binary (Content.getvalue ()), mime=mime)
Db.files.save (c) return
c[' _id ']
@app. Route ('/F /<fid> ')
def serve_file (FID):
try:
f = db.files.find_one (Bson.objectid.ObjectId (FID))
If f is None:
raise Bson.errors.InvalidId () return
flask. Response (f[' content '), mimetype= ' image/' + f[' mime ')
except bson.errors.InvalidId:
flask.abort (404)
Of course, in this case, there is no mime this property, so it is best to go to the MONGO shell with Db.files.drop () to clear off the original data.
2.6 According to the upload time to give not MODIFIED
using the HTTP 304 not MODIFIED can squeeze and exploit browser caching and save bandwidth as much as possible. This requires three actions
1), record the last upload of documents time
2. When the browser requests this file, plug a timestamp string into the request header
3, when the browser requests the file, from the request header to try to get this timestamp, if the time stamp with the file is consistent, it is directly 304
Embodied in the Code is
Import datetime
def save_file (f):
content = Stringio (F.read ())
try:
mime = image.open (content). Format.lower ()
if mime not in Allow_formats:
raise IOError ()
except IOError:
flask.abort ()
C = Dict (
content=bson.binary.binary (Content.getvalue ()),
Mime=mime,
Time=datetime.datetime.utcnow ( ),
Db.files.save (c) return
c[' _id ']
@app. Route ('/f/<fid> ')
def serve_file (FID):
try:
f = db.files.find_one (Bson.objectid.ObjectId (FID))
if F is None:
raise Bson.errors.InvalidId ()
if Flask.request.headers.get (' if-modified-since ') = = f[' time '].ctime (): Return
Flask. Response (status=304)
resp = flask. Response (f[' content '), mimetype= ' image/' + f[' mime ')
resp.headers[' last-modified '] = f[' time '].ctime ()
Return resp
except bson.errors.InvalidId:
flask.abort (404)
Then, get a script to add a timestamp to the picture already in the database.
Incidentally spit a trough, in fact NoSQL DB in this kind of environment does not show any advantage, use up with RDB almost no two.
2.7 The use of SHA-1 row weight
Unlike the cola in the fridge, for the most part you don't want a big wave of exactly the same picture in the database. Images, along with data such as their exiff, should be unique in the database, and it would be appropriate to use a slightly stronger hashing technique to detect.
The simplest way to achieve this is to create a SHA-1 unique index so that the database prevents the same thing from being put in.
Create a unique index in the table in MongoDB, execute (Mongo console)
Copy Code code as follows:
Db.files.ensureIndex ({sha1:1}, {unique:true})
If you have more than one record in your library, MongoDB will give you an error. This looks like a nice, harmless index operation is told that there is a duplicate value NULL in the database (in fact, the existing entries in the database do not have this attribute at all). Unlike General RDB, MongoDB specify NULL, or Non-existent property values are also an identical property value, so these phantom properties can cause a unique index to not be established.
The solution has three:
1 Delete all the data now (must be the test database to use this irresponsible way!)
2 Create a sparse index that does not require the Phantom attribute to be unique, but multiple null values will still be judged as duplicates (this can be done regardless of existing data)
3 Write a script run a database, all the data has been deposited into the SHA-1, recalculate, and then save in
The concrete procedure is arbitrary. Assuming that the problem is now fixed and that the index is ready, then there is the Python code thing left.
Import Hashlib
def save_file (f):
content = Stringio (F.read ())
try:
mime = image.open (content). Format.lower ()
if mime not in Allow_formats:
raise IOError ()
except IOError:
flask.abort
SHA1 = HASHLIB.SHA1 (Content.getvalue ()). Hexdigest ()
c = dict (
content=bson.binary.binary ( Content.getvalue ()),
Mime=mime,
time=datetime.datetime.utcnow (),
sha1=sha1,
)
try:
Db.files.save (c)
except Pymongo.errors.DuplicateKeyError: Pass return
c[' _id ']
The link on the upload file is no problem. However, according to the above logic, if an existing file is uploaded, returning c[' _id ' will be a nonexistent data ID. To fix this problem, it is best to return to SHA1 and, when accessing the file, modify the file to SHA-1 access instead of the ID.
The result of the final modification and the complete source code of this article are as follows:
Import hashlib import datetime import flask import Pymongo import bson.binary import bson.objectid import bson.errors fro M Cstringio import Stringio from pil import Image app = flask. Flask (__name__) App.debug = True db = Pymongo. Mongoclient (' localhost ', 27017). Test allow_formats = set ([' JPEG ', ' png ', ' gif ']) def save_file (f): content = Stringio (f. Read ()) Try:mime = Image.open (content). Format.lower () If MIME not in Allow_formats:raise IOError () exc EPT IOError:flask.abort (SHA1 = HASHLIB.SHA1 (Content.getvalue ()). Hexdigest () c = Dict (Content=bson.binar Y.binary (Content.getvalue ()), Mime=mime, Time=datetime.datetime.utcnow (), SHA1=SHA1,) TRY:DB.FILES.S Ave (c) Except Pymongo.errors.DuplicateKeyError:pass return SHA1 @app. Route ('/f/<sha1> ') def serve_file (SHA1 ): Try:f = Db.files.find_one ({' SHA1 ': SHA1}) If f is none:raise bson.errors.InvalidId () if Flask.req Uest.headers.get (' if-modified-since ')= = f[' time '].ctime (): Return flask. Response (status=304) resp = flask. Response (f[' content '), mimetype= ' image/' + f[' mime ') resp.headers[' last-modified '] = f[' time '].ctime () return res P except Bson.errors.InvalidId:flask.abort (404) @app. Route ('/upload ', methods=[' POST ']) def upload (): F = FLASK.R equest.files[' uploaded_file '] sha1 = Save_file (f) return Flask.redirect ('/f/' + str (SHA1)) @app. Route ('/') def index () : Return ' ' ' <!doctype html>
3, REF
Developing RESTful Web APIs with Python, flask and MongoDB
Http://www.slideshare.net/nicolaiarocci/developing-restful-web-apis-with-python-flask-and-mongodb
Https://github.com/nicolaiarocci/eve