基于tornado服务器的文件上传和下载

举报
天元浪子 发表于 2021/07/27 01:06:33 2021/07/27
【摘要】 文章目录 文件上传的服务端技术解析基于Ajax技术实现的文件上传客户端方法1:使用 ajaxfileupload.js方法2:仅依赖 jquery.js 文件下载的服务端技术解析 文件上传的服务端技术解析 常言到,爱有多深、恨有多切。tornado服务器就是这样一个矛盾体,它的缺点和它的优点一样,显著且强烈。有人认为,文件上传是tornado的重大缺...

文件上传的服务端技术解析

常言到,爱有多深、恨有多切。tornado服务器就是这样一个矛盾体,它的缺点和它的优点一样,显著且强烈。有人认为,文件上传是tornado的重大缺陷之一,它把用户上传的文件存放在内存中——这意味着多用户同时上传文件的话,内存的开销会急剧增加。不过我倒是觉得,这反倒让很多事情变得简单了,比如,你想对用户上传的内容做处理的话,不用再打开文件了,因为内容就在内存中。再者说,在tornado的异步机制下,我不确定真的能够多用户同时上传文件。这是一个有趣的问题。

好了,言归正传吧。假定文件上传的表单如下:

<form id="form_upload" action="/demo/upload" enctype="multipart/form-data" method="post"> <input type="file" name="want_to_upload_file_1"/><br/> <input type="file" name="want_to_upload_file_2"/><br/> <input type="submit" value="上传"/>
</form>

  
 
  • 1
  • 2
  • 3
  • 4
  • 5

机制是允许一次上传多个文件的。这里有几个问题需要特别说明一下。

  • 在提交表单之前,需要为form指定action和method的属性值,如果是上传文件,还要设置enctype=“multipart/form-data”。这三个属性,可以写在html中,也可以在submit之前用js的方法为其赋值

  • 文件浏览是file类型的input标签自备的功能,程序员无法在浏览器框架内操作本地文件。该标签的name属性,是用来区别于其他文件的标识,不是文件名,也不是文件对象,更不是文件内容

上面的表单被提交到/demo/upload(假定上传的第1个文件名为dqd.jpg,第2个文件名为intro.png),这个请求的对象中包含files字典,上传的全部文件的信息都包含在这个结构中。我们来看看这个request.files的真实面貌:

def post(self): print self.request.files.keys() # [u'want_to_upload_file_1', u'want_to_upload_file_2'] print type(self.request.files['want_to_upload_file_1']) # list,长度为1 meta_file_1 = self.request.files['want_to_upload_file_1'][0] print meta_file_1.keys() # ['body', 'content_type', 'filename'] print len(meta_file_1['body']) # 31492,文件长度 print meta_file_1['content_type'] # image/jpeg print meta_file_1['filename'] # dqd.jpg

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

有了这些素材,我们就可以无所不能地应对客户需求了。比如,不做任何处理,仅仅用原文件名保存在指定路径下(假设保存在/static/image/wiki目录下):

PROJECT_PATH = os.path.split(os.path.realpath(__file__))[0]
upload_path = os.path.join(PROJECT_PATH, 'static', 'image', 'wiki')
file_name = os.path.join(upload_path, meta_file_1['filename'])

with open(file_name, 'wb') as fp: fp.write(meta_file_1['body'])

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

很多时候,需要对用户上传的文件重命名(比如,用时间戳为文件名),但文件后缀名不变。

fn, ext = os.path.splitext(meta_file_1['filename'])
fn = '%d%s' % (time.time()*1000, ext)
file_name = os.path.join(upload_path, fn)

  
 
  • 1
  • 2
  • 3

如果需要对用户上传的文件类型做检查,请使用文件的content_type,而不是文件的扩展名,因为前者更规范。比如,JPEG类型的图片文件,其后缀名可能是.jpg|.jpeg|.JPG|.JPEG中的一种,而前者只有“image/jpeg”一种表示法。

关于文件的content_type,网上资料多如牛毛,请自行搜索。

处理用户上传的图片文件时,除了限制文件大小,有时候还要做缩放处理,甚至一并生成缩略图,此时就需要将文件内容转成易于处理的图像对象,比如,pil的Image

from PIL import Image
import StringIO

pilImg = Image.open(StringIO.StringIO(meta_file_1['body']))
print pilImg.size

  
 
  • 1
  • 2
  • 3
  • 4
  • 5

至于如何缩放、如何保存为文件,请自行检索相关资料。

基于Ajax技术实现的文件上传客户端

假定上传文件的表单是这样的:

<form id="form_upload" action="/demo/upload" enctype="multipart/form-data" method="post"> <input type="file" name="wiki_img" id="wiki_img" /><br /> <input id="doUpload" type="button" value="上传" />
</form>

  
 
  • 1
  • 2
  • 3
  • 4

方法1:使用 ajaxfileupload.js

<script src="jquery.js"></script>
<script src="ajaxfileupload.js"></script>
<script type="text/javascript"> $("#doUpload").click(function(){ $.ajaxFileUpload({ url:'/demo/upload', secureuri:false, fileElementId:'wiki_img', dataType: 'json', success: function(data){ alert(data); } }); });
</script>

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

方法2:仅依赖 jquery.js

<script src="jquery.js"></script>
<script type="text/javascript"> var formData = new FormData(); formData.append("file", $("#wiki_img")[0].files[0]); formData.append("filename", $("#wiki_img").val()); $.ajax({ url : '/demo/upload', type : 'POST', async : false, data : formData, processData : false, contentType : false, beforeSend:function(){ $("#upload_tips").html("正在进行,请稍候"); }, success : function(data) { alert(data); } });
</script>

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

文件下载的服务端技术解析

相对于上传,文件的下载就简单得多。只需要记住两点:开始前告诉浏览器要传输的文件类型,结束前对浏览器说拜拜。文件类型并不是固定的,需要根据文件的实际情况来选择。详情请自行检索。

def get(self): self.set_header ('Content-Type', 'application/octet-stream') with open(filename, 'rb') as f: while True: data = f.read(buf_size) if not data: break self.write(data) self.finish()

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

配合seek命令,可以实现更复杂的下载请求,比如,断点续传、分块下载、ajax异步请求等。

文章来源: xufive.blog.csdn.net,作者:天元浪子,版权归原作者所有,如需转载,请联系作者。

原文链接:xufive.blog.csdn.net/article/details/85289402

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。