溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何使用tornado服務(wù)器實現(xiàn)文件上傳和下載

發(fā)布時間:2020-09-10 16:43:26 來源:億速云 閱讀:704 作者:Leah 欄目:編程語言

如何使用tornado服務(wù)器實現(xiàn)文件上傳和下載?針對這個問題,這篇文章詳細(xì)介紹了相對應(yīng)的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

文件上傳的服務(wù)端技術(shù)解析

常言到,愛有多深、恨有多切。tornado服務(wù)器就是這樣一個矛盾體,它的缺點和它的優(yōu)點一樣,顯著且強烈。有人認(rèn)為,文件上傳是tornado的重大缺陷之一,它把用戶上傳的文件存放在內(nèi)存中——這意味著多用戶同時上傳文件的話,內(nèi)存的開銷會急劇增加。不過我倒是覺得,這反倒讓很多事情變得簡單了,比如,你想對用戶上傳的內(nèi)容做處理的話,不用再打開文件了,因為內(nèi)容就在內(nèi)存中。再者說,在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>

機制是允許一次上傳多個文件的。這里有幾個問題需要特別說明一下。

在提交表單之前,需要為form指定action和method的屬性值,如果是上傳文件,還要設(shè)置enctype=“multipart/form-data”。這三個屬性,可以寫在html中,也可以在submit之前用js的方法為其賦值。

文件瀏覽是file類型的input標(biāo)簽自備的功能,程序員無法在瀏覽器框架內(nèi)操作本地文件。該標(biāo)簽的name屬性,是用來區(qū)別于其他文件的標(biāo)識,不是文件名,也不是文件對象,更不是文件內(nèi)容。

上面的表單被提交到/demo/upload(假定上傳的第1個文件名為dqd.jpg,第2個文件名為intro.png),這個請求的對象中包含files字典,上傳的全部文件的信息都包含在這個結(jié)構(gòu)中。我們來看看這個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

有了這些素材,我們就可以無所不能地應(yīng)對客戶需求了。比如,不做任何處理,僅僅用原文件名保存在指定路徑下(假設(shè)保存在/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'])

很多時候,需要對用戶上傳的文件重命名(比如,用時間戳為文件名),但文件后綴名不變。

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,而不是文件的擴展名,因為前者更規(guī)范。比如,JPEG類型的圖片文件,其后綴名可能是.jpg|.jpeg|.JPG|.JPEG中的一種,而前者只有“image/jpeg”一種表示法。

關(guān)于文件的content_type,網(wǎng)上資料多如牛毛,請自行搜索。

處理用戶上傳的圖片文件時,除了限制文件大小,有時候還要做縮放處理,甚至一并生成縮略圖,此時就需要將文件內(nèi)容轉(zhuǎn)成易于處理的圖像對象,比如,pil的Image。

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

至于如何縮放、如何保存為文件,請自行檢索相關(guān)資料。

基于Ajax技術(shù)實現(xiàn)的文件上傳客戶端

假定上傳文件的表單是這樣的:

<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:使用 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>

方法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>

文件下載的服務(wù)端技術(shù)解析

相對于上傳,文件的下載就簡單得多。只需要記住兩點:開始前告訴瀏覽器要傳輸?shù)奈募愋停Y(jié)束前對瀏覽器說拜拜。文件類型并不是固定的,需要根據(jù)文件的實際情況來選擇。詳情請自行檢索。

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()

配合seek命令,可以實現(xiàn)更復(fù)雜的下載請求,比如,斷點續(xù)傳、分塊下載、ajax異步請求等。

關(guān)于如何使用tornado服務(wù)器實現(xiàn)文件上傳和下載問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI