python 实现文件上传下载

介绍

  • 实现支持文件上传、下载、删除的api接口(支持多文件上传)
  • 使用mongo 做为后端存储
  • 支持异步

一.使用python tornado实现

  • python 版本 2.7
  • tornado版本 4.3
# -*- coding: utf-8 -*-
__Author__ = 'Traveler'

import json
import datetime

import tornado.httpserver
import tornado.ioloop
import tornado.web
import tornado.gen
#
from tornado.concurrent import run_on_executor
from werkzeug import secure_filename
from concurrent import futures
#
import pymongo
import gridfs
from bson.objectid import ObjectId
from cStringIO import StringIO

import logging
# import logging.handlers
logging.basicConfig(level=logging.INFO,format='%(asctime)s %(filename)s %(module)s %(funcName)s %(process)d %(thread)d %(threadName)s [line:%(lineno)d] [%(levelname)s] %(message)s')

log = logging.getLogger()

# 定义 storage Handler
class StorageFiles(tornado.web.RequestHandler):
    def initialize(self):
        self.mongo = pymongo.MongoClient('127.0.0.1', 27017)
        self.db = self.mongo['storage']
        self.fs = gridfs.GridFS(self.db, 'files')
        self.executor = futures.ThreadPoolExecutor(max_workers=24)
    #
    def Time(self):
        date1 = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        date2 = datetime.datetime.now().strftime('.%f')[:4]
        date = date1 + date2
        return date
    # 只允许上传下列后缀的文件
    def __allowed_file(self, filename):
        ALLOWED_EXTENSIONS = set(['txt', 'conf', 'ini', 'cf', 'yml', 'xml', 'pdf', 'png', 'jpg', 'jpeg', 'gif', 'json', 'gz', 'rar', 'unzip','tgz'])
        return '.' in filename and filename.split('.', 1)[1] in ALLOWED_EXTENSIONS
    #
    @tornado.web.asynchronous
    @tornado.gen.coroutine
    def get(self):
        yield self._get()

    @run_on_executor
    def _get(self):
        filename = self.get_argument('filename')
        files_id = self.get_argument('files_id')
        #
        files = self.fs.get(ObjectId(files_id))
        data = files.read()
        #
        self.set_header('Content-Type', 'application/octet-stream;charset=utf-8')
        self.set_header('Content-Disposition', 'attachment; filename=' + filename)
        self.write(data)

    #
    @tornado.web.asynchronous
    @tornado.gen.coroutine
    def post(self):
        yield self._post()

    @run_on_executor
    def _post(self):
        begin_time = self.Time()
        #
        files_meta = self.request.files['files']
        #
        uri = []
        for meta in files_meta:
            if meta and self.__allowed_file(meta['filename']):
                filename = secure_filename(meta['filename'])  # 获取一个安全的文件名,仅支持ASAII字符,如果需要文件包含中文,可注释此行代码
                # files_meta.save(os.path.join(self._upload_folder,filename))
                data = StringIO(meta['body']).getvalue()
                files_id = self.fs.put(data=data, filename=filename)
                args = '?filename=%s&files_id=%s' % (filename, files_id)
                uri.append(self.reverse_url(name='StorageFiles') + args)
            else:
                self.write(json.dumps({'error': 'The format is not correct'}))
        #
        runtime = {'begin_time': begin_time, 'end_time': self.Time()}
        res = {}
        res['status'] = 'ok'
        res['runtime'] = runtime
        # res['uri'] = {'download': download, 'delete': delete}
        res['uri'] = uri
        #
        self.write(json.dumps(res,sort_keys=True,indent=4,separators=(',',':')))

    #
    @tornado.web.asynchronous
    @tornado.gen.coroutine
    def delete(self):
        yield self._delete()

    @run_on_executor
    def _delete(self):
        begin_time = self.Time()
        files_id = self.get_argument('files_id')
        self.fs.delete(ObjectId(files_id))
        # os.remove(self._upload_folder + '/' + filename)
        runtime = {'begin_time': begin_time, 'end_time': self.Time()}
        res = {}
        res['status'] = 'ok'
        res['runtime'] = runtime
        self.write(json.dumps(res,sort_keys=True,indent=4,separators=(',',':')))

#
class Application(tornado.web.Application):
    def __init__(self):
        handlers = [
            (r'/storage',StorageFiles,{},'StorageFiles')
        ]
        #
        settings = {
            'debug': True,
        }
        #
        super(Application,self).__init__(handlers=handlers,**settings)
#
def run():
    import tornado.options
    from tornado.options import define, options
    define('port', default=8000, help='run on the given port', type=int)
    tornado.options.parse_command_line()
    app = Application()
    # app.listen(options.port,address='0.0.0.0')
    tornado.httpserver.HTTPServer(app).listen(options.port)
    log.info('listen http://127.0.0.1:8000')
    tornado.ioloop.IOLoop.instance().start()

if __name__ == '__main__':
    run()

二.使用python flask实现

  • python 版本 2.7
  • flask 版本0.10.1
  • Flask-RESTful 版本 (0.3.5)

ps: 其实flask-restful 的工作模式与tornado比较接近了.

# -*- coding: utf-8 -*-
#
from flask import Flask
from flask import jsonify
from flask import request
from flask import make_response
from werkzeug import secure_filename
#
from flask.ext.restful import Api
from flask.ext.restful import Resource
from flask.ext.restful import fields
#
import datetime
#
import pymongo
from bson.objectid import ObjectId
import gridfs
from cStringIO import StringIO

#
class StorageFiles(Resource):
    def __init__(self):
        super(StorageFiles, self).__init__()
        # self._upload_folder = '/tmp/storage'
        self.mongo = pymongo.MongoClient('127.0.0.1',27017)
        self.db = self.mongo['storage']
        self.fs = gridfs.GridFS(self.db,'files')

    def Time(self):
        date1 = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        date2 = datetime.datetime.now().strftime('.%f')[:4]
        date = date1 + date2
        return date

    #
    def __allowed_file(self,filename):
        ALLOWED_EXTENSIONS = set(['txt', 'conf', 'ini', 'cf', 'yml', 'xml', 'pdf', 'png', 'jpg', 'jpeg', 'gif', 'json', 'gz', 'rar','unzip', 'tgz'])
        return '.' in filename and filename.split('.',1)[1] in ALLOWED_EXTENSIONS
    #
    def get(self):
        filename = request.values['filename']
        files_id = request.values['files_id']
        #
        files = self.fs.get(ObjectId(files_id))
        data = files.read()

        headers = {}
        headers['Content-Type'] = 'application/octet-stream; charset=utf-8'
        headers['Content-Disposition'] = 'attachment; filename=' + filename

        return make_response(data,200,headers)
    #
    def post(self):
        begin_time = self.Time()
        # files = request.files['files']
        files_meta = request.files.getlist('files')
        #
        uri = []
        for meta in files_meta:
            filename = meta.filename
            if filename and self.__allowed_file(filename):
                filename = secure_filename(filename)
                # files.save(os.path.join(self._upload_folder,filename))
                data = StringIO(meta.read()).getvalue()
                files_id = self.fs.put(data=data,filename=filename)
                uri.append(fields.url_for(endpoint='StorageFiles',files_id=files_id,filename=filename))
            else:
                return make_response(jsonify({'error':'The format is not correct'}),403)
        #
        runtime = {'begin_time': begin_time, 'end_time': self.Time()}
        res = {}
        res['status'] = 'ok'
        res['runtime'] = runtime
        res['uri'] = uri
        #
        return make_response(jsonify(res),200)
    #
    def put(self):
        pass
    #
    def delete(self):
        begin_time = self.Time()
        files_id = request.values['files_id']

        self.fs.delete(ObjectId(files_id))
        # os.remove(self._upload_folder + '/' + filename)
        runtime = {'begin_time': begin_time, 'end_time': self.Time()}
        res = {}
        res['status'] = 'ok'
        res['runtime'] = runtime
        return make_response(jsonify(res), 200)
#
app = Flask(__name__)

api = Api(app=app)
api.add_resource(StorageFiles,'/storage',endpoint='StorageFiles')

#
if __name__ == '__main__':
    app.run(port=8000,debug=True)


测试

文件:test.txt

上传:

curl -i -F 'files=@test.txt' http://127.0.0.1:8000/storage

下载:

curl 'http://127.0.0.1:8000/storage?filename=test.txt&files_id=57a856942f4dfd2f9923ba26'

删除:

curl -X DELETE 'http://127.0.0.1:8000/storage?filename=test.txt&files_id=57a856942f4dfd2f9923ba26'

备注: 下载和删除的url改成上传后返回的uri.

转载于:https://my.oschina.net/Traveller/blog/729606

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值