Vue實(shí)現(xiàn)帶進(jìn)度條的文件拖動上傳

1. 基本界面

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link  rel="stylesheet">
    <script src="https://cdn.bootcss.com/vue/2.5.13/vue.min.js"></script>
    <style>
        .dropbox {
            border: .25rem dashed #007bff;
            min-height: 5rem;
        }
    </style>
    <title>Document</title>
</head>
<body>
<div id="app" class="m-5">
    <div class="dropbox p-3">
        <h2 class="text-center">把要上傳的文件拖動到這里</h2>
    </div>
</div>
<script>
    new Vue({
        el: '#app',
        data: {},
        methods: {},
        mounted: function () {}
    });
</script>
</body>
</html>
image.png

2. 檢測拖動事件

首先讓頁面支持文件拖拽便斥,在 Vue 的 mounted() 函數(shù)中添加代碼:

mounted: function () {
    var dropbox = document.querySelector('.dropbox');
    dropbox.addEventListener('dragenter', this.onDrag, false);
    dropbox.addEventListener('dragover', this.onDrag, false);
    dropbox.addEventListener('drop', this.onDrop, false);
}

當(dāng)把文件拖動到瀏覽器的拖動區(qū)域時(shí)灸拍,會觸發(fā)三種事件:

  1. 文件第一次進(jìn)入拖動區(qū)時(shí)菱鸥,觸發(fā) dragenter 事件
  2. 文件在拖動區(qū)來回拖拽時(shí)铆帽,不斷觸發(fā) dragover 事件
  3. 文件已經(jīng)在拖動區(qū)胞枕,并松開鼠標(biāo)時(shí)压真,觸發(fā) drop 事件

實(shí)現(xiàn)拖動上傳娩嚼,我們只需要關(guān)心 drop 事件。不過另外兩個(gè)事件也需要監(jiān)聽滴肿,目的是阻止瀏覽器默認(rèn)行為岳悟。如果不阻止,那么把文件拖到瀏覽器時(shí)泼差,瀏覽器就會自動下載這個(gè)文件(默認(rèn)行為)贵少,drop 事件觸發(fā)不出來。

事件的監(jiān)聽函數(shù)添加在 Vue 的 methods 對象中:

methods: {
    uploadFile: function (file) {
        console.log(file);
    },
    onDrag: function (e) {
        e.stopPropagation();
        e.preventDefault();
    },
    onDrop: function (e) {
        e.stopPropagation();
        e.preventDefault();
        var dt = e.dataTransfer;
        for (var i = 0; i !== dt.files.length; i++) {
            this.uploadFile(dt.files[i]);
        }
    }
},

onDrop() 函數(shù)中堆缘,通過 e.dataTransfer.files 可以拿到用戶拖動到瀏覽器的文件的基本信息春瞬,uploadFile() 函數(shù)現(xiàn)在只這些信息打印了出來,可以了解到套啤,拖動到瀏覽器的每個(gè)文件都是一個(gè) File 對象:

image.png

3. 處理拖動事件

現(xiàn)在宽气,我們要給 uploadFile() 函數(shù)增加功能,實(shí)現(xiàn)拖動文件時(shí)潜沦,拖動區(qū)出現(xiàn)文件名和一個(gè)上傳進(jìn)度條萄涯。

首先在 Vue 的 data 對象中定義 files 屬性,用來保存所有拖動到瀏覽器中文件的名稱唆鸡。然后在uploadFile() 函數(shù)每當(dāng)被調(diào)用時(shí)涝影,把文件名和上傳進(jìn)度保存到 files 中:

data: {
    files: []
},
methods: {
    uploadFile: function (file) {
        var item = {
            name: file.name,
            uploadPercentage: 67
        };
        this.files.push(item);
    },
}

上傳進(jìn)度的功能在后面再介紹,先寫一個(gè)固定值争占。

相應(yīng)地燃逻,在HTML代碼中序目,用 v-for 關(guān)鍵字顯示 files 的每一項(xiàng):

<div class="dropbox p-3">
    <h2 class="text-center">把要上傳的文件拖動到這里</h2>
    <div class="border m-2 d-inline-block p-4" style="width:15rem" v-for="file in files">
        <h5 class="mt-0">{{ file.name }}</h5>
        <div class="progress">
            <div class="progress-bar progress-bar-striped"
                    :style="{ width: file.uploadPercentage+'%' }"></div>
        </div>
    </div>
</div>

而且,“把要上傳的文件拖動到這里” 的提示只在拖動區(qū)沒有文件的時(shí)候才顯示:

<h2 v-if="files.length===0" class="text-center">把要上傳的文件拖動到這里</h2>

這樣伯襟,拖動效果就有了:

image.png

4. 文件上傳

接下來實(shí)現(xiàn)真正的文件上傳猿涨,繼續(xù)往 uploadFile() 函數(shù)添加代碼:

uploadFile: function (file) {
    var item = {
        name: file.name,
        uploadPercentage: 67
    };
    this.files.push(item);
    var fd = new FormData();
    fd.append('myFile', file);

    var xhr = new XMLHttpRequest();
    xhr.open('POST', 'upload.php', true);
    xhr.send(fd);
},

這里用到了 FormData,把要上傳的文件附在了 FormData 上姆怪,并通過AJAX方式發(fā)送給PHP端叛赚。PHP端代碼:

if (isset($_FILES['myFile'])) {
    move_uploaded_file($_FILES['myFile']['tmp_name'], 'uploads/' . $_FILES['myFile']['name']);
    echo 'OK';
} else {
    echo 'No file specified';
}

現(xiàn)在刷新下頁面,把電腦上的兩個(gè)文件拖到瀏覽器中稽揭,PHP端會接收并保存文件到 uploads 目錄:

image.png

提示:如果拖放時(shí)遇到PHP返回了No file specified俺附,或者 $_FILESNULL 的情況時(shí),有可能是PHP限制了POST請求最大字節(jié)溪掀,或者限制了上傳文件的體積事镣。這時(shí)候需要調(diào)整下php.ini中的這兩個(gè)配置:

  • post_max_size = 20M // POST請求的最大字節(jié)數(shù)
  • upload_max_filesize = 20M // 上傳文件的最大體積

進(jìn)度條的展示

基本的上傳功能完成了,最后我們來完成進(jìn)度條揪胃。每當(dāng)AJAX請求發(fā)送了一段時(shí)間的數(shù)據(jù)時(shí)璃哟,都會生成一個(gè) progress 事件,我們可以監(jiān)聽 progress 事件來知道當(dāng)前的上傳進(jìn)度:

uploadFile: function (file) {
    ...
    xhr.upload.addEventListener('progress', function (e) {
        item.uploadPercentage = Math.round((e.loaded * 100) / e.total);
    }, false);
    xhr.send(fd);
},

e.loaded 代表當(dāng)前AJAX發(fā)送了多少字節(jié)只嚣,e.total 代表AJAX總共要發(fā)送多少字節(jié)。通過這兩個(gè)屬性可以計(jì)算上傳進(jìn)度的百分比艺沼。

這樣册舞,一個(gè)帶進(jìn)度條的文件拖動上傳功能就完成了。

附:完整代碼

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link  rel="stylesheet">
    <script src="https://cdn.bootcss.com/vue/2.5.13/vue.min.js"></script>
    <style>
        .dropbox {
            border: .25rem dashed #007bff;
            min-height: 5rem;
        }
    </style>
    <title>Document</title>
</head>
<body>
<div id="app" class="m-5">
    <div class="dropbox p-3">
        <h2 v-if="files.length===0" class="text-center">把要上傳的文件拖動到這里</h2>
        <div class="border m-2 d-inline-block p-4" style="width:15rem" v-for="file in files">
            <h5 class="mt-0">{{ file.name }}</h5>
            <div class="progress">
                <div class="progress-bar progress-bar-striped"
                     :style="{ width: file.uploadPercentage+'%' }"></div>
            </div>
        </div>
    </div>
</div>
<script>
    new Vue({
        el: '#app',
        data: {
            files: []
        },
        methods: {
            uploadFile: function (file) {
                var item = {
                    name: file.name,
                    uploadPercentage: 0
                };
                this.files.push(item);
                var fd = new FormData();
                fd.append('myFile', file);

                var xhr = new XMLHttpRequest();
                xhr.open('POST', 'upload.php', true);
                xhr.upload.addEventListener('progress', function (e) {
                    item.uploadPercentage = Math.round((e.loaded * 100) / e.total);
                }, false);
                xhr.send(fd);
            },
            onDrag: function (e) {
                e.stopPropagation();
                e.preventDefault();
            },
            onDrop: function (e) {
                e.stopPropagation();
                e.preventDefault();
                var dt = e.dataTransfer;
                for (var i = 0; i !== dt.files.length; i++) {
                    this.uploadFile(dt.files[i]);
                }
            }
        },
        mounted: function () {
            var dropbox = document.querySelector('.dropbox');
            dropbox.addEventListener('dragenter', this.onDrag, false);
            dropbox.addEventListener('dragover', this.onDrag, false);
            dropbox.addEventListener('drop', this.onDrop, false);
        }
    });
</script>
</body>
</html>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末障般,一起剝皮案震驚了整個(gè)濱河市调鲸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌挽荡,老刑警劉巖藐石,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異定拟,居然都是意外死亡于微,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門青自,熙熙樓的掌柜王于貴愁眉苦臉地迎上來株依,“玉大人,你說我怎么就攤上這事延窜×低螅” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵逆瑞,是天一觀的道長荠藤。 經(jīng)常有香客問我伙单,道長,這世上最難降的妖魔是什么哈肖? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任吻育,我火速辦了婚禮,結(jié)果婚禮上牡彻,老公的妹妹穿的比我還像新娘扫沼。我一直安慰自己,他們只是感情好庄吼,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布缎除。 她就那樣靜靜地躺著,像睡著了一般总寻。 火紅的嫁衣襯著肌膚如雪器罐。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天渐行,我揣著相機(jī)與錄音轰坊,去河邊找鬼。 笑死祟印,一個(gè)胖子當(dāng)著我的面吹牛肴沫,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蕴忆,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼颤芬,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了套鹅?” 一聲冷哼從身側(cè)響起站蝠,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎卓鹿,沒想到半個(gè)月后菱魔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡吟孙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年澜倦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片杰妓。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡肥隆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出稚失,到底是詐尸還是另有隱情栋艳,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布句各,位于F島的核電站吸占,受9級特大地震影響晴叨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜矾屯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一兼蕊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧件蚕,春花似錦孙技、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至妄痪,卻和暖如春哈雏,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背衫生。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工裳瘪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人罪针。 一個(gè)月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓彭羹,卻偏偏與公主長得像,于是被迫代替她去往敵國和親泪酱。 傳聞我的和親對象是個(gè)殘疾皇子派殷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容