当前位置: 首页 > 图灵资讯 > 行业资讯> 基于tornado服务器实现文件上传和下载

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

发布时间:2025-04-06 15:47:49

文件上传的服务终端技术分析

俗话说,爱有多深,恨有多深。tornado服务器就是这样一个矛盾,它的缺点和它的优点一样明显而强大。有些人认为,文件上传是tornado的主要缺陷之一,它将用户上传的文件存储在内存中——这意味着如果多个用户同时上传文件,内存成本将急剧增加。但我认为这使得很多事情变得简单,例如,如果你想处理用户上传的内容,你不必打开文件,因为内容在内存中。此外,在tornado的异步机制下,我不确定多个用户能否同时上传文件。这是一个有趣的问题。

好了,言归正传。假设文件上传的表格如下:

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

该机制允许一次上传多个文件。这里有几个问题需要特别解释。

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

文件浏览是file类型的input标签的功能,程序员无法在浏览器框架内操作本地文件。该标签的name属性用于识别不同于其他文件的标志,不是文件名称、文件对象或文件内容。

上表单已提交/demo/upload(假设上传的第一个文件是dqd.jpg,第二个文件是intro.png),请求对象包含files字典,上传的所有文件信息都包含在此结构中。让我们来看看这个request.files的真实面貌:

defpost(self):
printself.request.files.keys()#[u'want_to_upload_file_1',u'want_to_upload_file_2']
printtype(self.request.files['want_to_upload_file_1'])#list,长度为1
meta_file_1=self.request.files['want_to_upload_file_1'][0]
printmeta_file_1.keys()#['body','content_type','filename']
printlen(meta_file_1['body'])#31492,文件长度
printmeta_file_1['content_type']#image/jpeg
printmeta_file_1['filename']#dqd.jpg

有了这些材料,我们就可以无所不能地满足客户的需求。例如,不做任何处理,只在指定路径下使用原始文件名(假设存在/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'])
withopen(file_name,'wb')asfp:
fp.write(meta_file_1['body'])

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

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

如需检查用户上传的文件类型,请使用文件的content_type,而不是文件的扩展名,因为前者更标准化。例如,JPEG类型的图片文件的后缀名称可能是.jpg|.jpeg|.JPG|.其中一个JPEG,而前者只有“image/jpeg一种表达法。

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

在处理用户上传的图片文件时,除了限制文件的大小外,有时还需要缩放,甚至生成缩略图。此时,有必要将文件内容转换为易于处理的图像对象,例如pilImage。

fromPILimportImage
importStringIO
pilImg=Image.open(StringIO.StringIO(meta_file_1['body']))
printpilImg.size

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

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

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

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

方法1:使用 ajaxfileupload.js

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

方法2:仅依赖 jquery.js

<scriptsrc="jquery.js"></script>
<scripttype="text/javascript">
varformData=newFormData();
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>

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

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

defget(self):
self.set_header('Content-Type','application/octet-stream')
withopen(filename,'rb')asf:
whileTrue:
data=f.read(buf_size)
ifnotdata:
break
self.write(data)
self.finish()

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

python学习网络上有很多python培训视频,欢迎在线学习习!

相关文章

python3兼容python2吗

python3兼容python2吗

2025-05-09
python3 whl怎么安装

python3 whl怎么安装

2025-05-09
python 字典怎么提取value

python 字典怎么提取value

2025-05-09
python 怎样计算字符串的长度

python 怎样计算字符串的长度

2025-05-09
python 怎么样反向输出字符串

python 怎么样反向输出字符串

2025-05-09
python 怎么判断字符串开头

python 怎么判断字符串开头

2025-05-09