国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 網(wǎng)站 > WEB開發(fā) > 正文

使用AJAX實現(xiàn)文件拖拽上傳功能詳解

2024-04-27 15:19:18
字體:
供稿:網(wǎng)友

關(guān)注微信公眾號“編碼很酷”

概述

對于微云、百度云等網(wǎng)盤提供的文件存儲服務(wù)而言,文件上傳是一個重要功能。文件上傳的方式主要有兩種:二進制數(shù)據(jù)上傳、表單上傳。本文會詳細解析表單上傳的協(xié)議規(guī)范,前端上傳文件的兩種方式:對話框選擇方式、拖拽選擇方式,服務(wù)端接收上傳的文件以及文件上傳功能的技巧等。

表單上傳協(xié)議詳解

RFC1867(https://www.ietf.org/rfc/rfc1867.txt) 規(guī)范了表單上傳的協(xié)議格式。下面給出一個例子,用Fiddler抓包工具,抓取同時上傳兩個字符串內(nèi)容和一個文本文件的HTTP請求,獲取的請求內(nèi)容如下:

POST http://localhost:8080/Server/uploadfile HTTP/1.1Host: localhost:8080Connection: keep-aliveContent-Length: 391Cache-Control: no-cacheOrigin: Chrome-extension://fdmmgilgnpjigdojojpjoooidkmcomcmUser-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryuA8AsEvrgV5BUqe5Accept: */*Accept-Encoding: gzip, deflate, brAccept-Language: zh-CN,zh;q=0.8------WebKitFormBoundaryuA8AsEvrgV5BUqe5Content-Disposition: form-data; name="file1"value1------WebKitFormBoundaryuA8AsEvrgV5BUqe5Content-Disposition: form-data; name="file2"; filename="test2.txt"Content-Type: text/plainhello world------WebKitFormBoundaryuA8AsEvrgV5BUqe5Content-Disposition: form-data; name="file3"value3------WebKitFormBoundaryuA8AsEvrgV5BUqe5--

根據(jù)HTTP協(xié)議規(guī)范,每個請求頭后面都需要追加回車和換行符(/r/n)。消息頭和消息體之間也需要插入回車和換行符,忽略其它的請求頭部,表單上傳的格式可簡化成如下代碼,方便描述。

Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryuA8AsEvrgV5BUqe5回車換行回車換行------WebKitFormBoundaryuA8AsEvrgV5BUqe5回車換行Content-Disposition: form-data; name="file2"; filename="test2.txt"回車換行Content-Type: text/plain回車換行回車換行hello world回車換行------WebKitFormBoundaryuA8AsEvrgV5BUqe5回車換行Content-Disposition: form-data; name="file3"回車換行回車換行value3回車換行------WebKitFormBoundaryuA8AsEvrgV5BUqe5--回車換行

1.添加表單描述頭部

使用表單上傳功能,需要在頭部添加如下代碼,其中“multipart/form-data”表示請求上傳的內(nèi)容類型為表單,“boundary”表示分隔符,用于分割表單里面的每項內(nèi)容。

Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryuA8AsEvrgV5BUqe5回車換行

2.添加表單內(nèi)容

表單中每項內(nèi)容的類型無外乎就兩種,一種是文本類型,另外一種是文件類型。每項內(nèi)容之間需要用“–+boundary+回車換行”進行分割,緊接著分隔符的代碼用于描述內(nèi)容配置。其中文本類型的內(nèi)容需要添加如下格式的代碼:

------WebKitFormBoundaryuA8AsEvrgV5BUqe5回車換行Content-Disposition: form-data; name="file3"回車換行回車換行value3回車換行

“name”用于描述表單的字段名稱,兩個回車換行之后就是這個字段的值。文件類型的內(nèi)容跟文本類型對比多了兩個字段,“filename”用于描述上傳的文件的名稱,“Content-Type”用于描述上傳的文件類型(文件的MIME),文件類型的內(nèi)容需要添加如下格式的代碼:

------WebKitFormBoundaryuA8AsEvrgV5BUqe5回車換行Content-Disposition: form-data; name="file2"; filename="test2.txt"回車換行Content-Type: text/plain回車換行回車換行hello world回車換行

添加完表單的每項內(nèi)容之后,需要在后面追加“–+boundary+–+回車換行”,完成表單內(nèi)容的拼接。

前端選擇文件上傳的兩種方式

1.對話框選擇方式上傳

實現(xiàn)對話框選擇文件,會用到如下代碼:

<form action="http://localhost:8080/Server/uploadfile" method="post" enctype="multipart/form-data"> <br> 文件: <input type="file" name="image"> <br> <input type="submit" value="上傳"> </form>

其中action字段為文件上傳的接口地址,enctype需要定義為“multipart/form-data”、input標簽的type屬性的值為“file”,對應(yīng)的name為表單的字段名稱。

2.拖拽選擇方式上傳

要實現(xiàn)這個功能,可借助Html5新增的“Drag and drop”功能。W3C官方文檔為:https://www.w3.org/TR/2014/CR-html5-20140731/editing.html。 利用它,我們可以知道文件何時被拖動到目標區(qū)域、文件何時離開目標區(qū)域、有哪些文件被拖到了目標區(qū)域。接下來就具體聊聊“Drag and drop”功能。

如何知道文件何時拖動到目標區(qū)域又何時離開目標區(qū)域?

HTML中的每個標簽都能夠設(shè)置跟拖動相關(guān)的事件,拖動事件的回調(diào)函數(shù)解釋如下:

事件 描述
ondragstart 拖動操作開始時調(diào)用(部分瀏覽器不回調(diào)此方法)
ondrag 拖動過程中調(diào)用(部分瀏覽器不回調(diào)此方法)
ondragenter 剛拖動到目標元素區(qū)域時調(diào)用
ondragover 在目標元素區(qū)域內(nèi)拖動時調(diào)用,此方法會隔一段時間調(diào)用一次
ondragleave 拖動離開目標元素區(qū)域時調(diào)用
ondragend 拖動結(jié)束時回調(diào)(部分瀏覽器不回調(diào)此方法)
ondrop 在目標元素區(qū)域內(nèi)放開拖動內(nèi)容時調(diào)用

注冊事件可以使用如下代碼:

//element可以為HTML標簽、documentelement.ondragstart = function(ev) { console.log('ondragstart');}

注意:

瀏覽器默認在拖放完成時會打開所拖放的文件,正確的做法是要調(diào)用事件對象的PReventDefault方法用來阻止事件的默認動作的執(zhí)行。

//element可以為HTML標簽、documentelement.ondragover = function(ev) { ev.preventDefault(); //do something}

如何獲取拖動的文件?

上面所列舉的回調(diào)函數(shù),每個回調(diào)函數(shù)里面都有一個參數(shù)DragEvent,DragEvent的接口定義語言描述如下:

interface DragEvent : MouseEvent { readonly attribute DataTransfer? dataTransfer;};

可以看到拖動事件接口繼承于鼠標事件接口,其中有個屬性dataTransfer(數(shù)據(jù)傳輸者)用于傳輸拖動的內(nèi)容,DataTransfer的接口定義語言如下:

interface DataTransfer { attribute DOMString dropEffect; attribute DOMString effectAllowed; readonly attribute DataTransferItemList items; void setDragImage(Element image, long x, long y); /* old interface */ readonly attribute DOMString[] types; DOMString getData(DOMString format); void setData(DOMString format, DOMString data); void clearData(optional DOMString format); readonly attribute FileList files;};

其中的setData方法用于設(shè)置要傳輸?shù)膬?nèi)容,getData方法用于獲取傳輸?shù)膬?nèi)容。當要實現(xiàn)從一個元素中拖動內(nèi)容到另外一個元素區(qū)域時可以使用者兩個方法。拖動文件時值需要使用files屬性,其值被瀏覽器設(shè)置進去了,因此只要獲取即可。那么獲取files的最佳時機是什么時候,當然是在ondrop方法回調(diào)時最佳。

dz.ondrop = function(ev) { //阻止瀏覽器默認打開文件的操作 ev.preventDefault(); //表單上傳文件...}

如何上傳獲取到的文件?

使用Ajax即可通過表單方式上傳文件,附上前端拖拽上傳的完整代碼。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh-CN"><head> <title>HTML5拖拽上傳</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="description" content="" /> <meta name="keyWords" content="" /> <style type="text/CSS"> #dropzone { width: 300px; height: 300px; border: 2px dashed gray; } #dropzone.over { width: 300px; height: 300px; border: 2px dashed red; } </style></head><body> <div id="dropzone" dropEffect="link"></div></body><script type="text/javascript">function uploadFile(formData) { var xhr = new xmlhttpRequest(); xhr.open('POST', 'http://localhost:8080/Server/uploadfile', true); xhr.send(formData);}var dz = document.getElementById('dropzone');dz.ondragover = function(ev) { //阻止瀏覽器默認打開文件的操作 ev.preventDefault(); this.className = 'over';}dz.ondragleave = function() { this.className = '';}dz.ondrop = function(ev) { this.className = ''; //阻止瀏覽器默認打開文件的操作 ev.preventDefault(); //表單上傳文件 var formData = new FormData(); formData.append('file', ev.dataTransfer.files[0]); uploadFile(formData);}</script></html>

服務(wù)端處理上傳的文件

服務(wù)端代碼本人采用JAVAEE開發(fā),文件上傳使用到commons fileupload組件:https://commons.apache.org/proper/commons-fileupload/download_fileupload.cgi,commons fileupload依賴common io庫。

完整代碼如下:

package com.servlet;import java.io.File;import java.io.IOException;import java.util.Iterator;import java.util.List;import javax.servlet.ServletContext;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.apache.commons.fileupload.FileItem;import org.apache.commons.fileupload.FileUploadException;import org.apache.commons.fileupload.disk.DiskFileItemFactory;import org.apache.commons.fileupload.servlet.ServletFileUpload;@WebServlet(name = "uploadfile", urlPatterns = "/uploadfile")public class UploadFileServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { ServletContext servletContext = this.getServletConfig() .getServletContext(); // Create a factory for disk-based file items DiskFileItemFactory factory = new DiskFileItemFactory(); // Set factory constraints String path = "D://upload"; File uploadDir = new File(path); if (!uploadDir.exists()) { uploadDir.mkdirs(); } factory.setRepository(new File(uploadDir.getAbsolutePath())); // Create a new file upload handler ServletFileUpload upload = new ServletFileUpload(factory); // Set overall request size constraint upload.setSizeMax(-1); // Parse the request List<FileItem> items = upload.parseRequest(req); // Process the uploaded items Iterator<FileItem> iter = items.iterator(); while (iter.hasNext()) { FileItem item = iter.next(); if (item.isFormField()) { // 普通表單數(shù)據(jù) } else { // 文件表單數(shù)據(jù) item.write(new File(uploadDir.getAbsolutePath() + File.separator + item.getName())); } } } catch (Exception e) { e.printStackTrace(); } }}

文件上傳功能的一些技巧

1.實現(xiàn)文件秒傳功能

微云、百度云就含有文件秒傳功能,其實現(xiàn)原理其實很簡單,文件可以用其md5來區(qū)分差異性。上傳文件時計算文件的MD5,只要服務(wù)器上存在相同MD5值的文件,則不會真正的上傳文件,而是把網(wǎng)盤上文件的索引存儲到當前用戶信息中。所以一般網(wǎng)盤上不會出現(xiàn)MD5值相同的文件。

2.防止可執(zhí)行文件注入攻擊

以tomcat服務(wù)器為例,WEB-INF目錄可以被瀏覽器訪問。如果用戶將可執(zhí)行的文件如xx.jsp上傳到這個目錄,里面編寫了刪除文件目錄的代碼,則當瀏覽器訪問這個xx.jsp文件時,這段惡意代碼就會被執(zhí)行,這顯然是惡意攻擊。為了阻止這種行為,正確的做法是過濾掉可執(zhí)行文件,不讓其上傳,這種判斷前端和后端都需要做,前端做的目的是可以減輕服務(wù)端的判斷壓力,后端做是為了阻止模擬的HTTP請求上傳惡意文件。

更多

長按下圖->識別圖中二維碼或者掃一掃關(guān)注我的公眾號。

微信公眾號


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 扎囊县| 民权县| 抚顺县| 东乡| 青岛市| 新建县| 阿克苏市| 班戈县| 邵东县| 疏附县| 池州市| 綦江县| 岚皋县| 永善县| 玉田县| 神池县| 清原| 玉屏| 香格里拉县| 胶南市| 临西县| 谢通门县| 九寨沟县| 吴江市| 门头沟区| 云梦县| 酉阳| 五常市| 乐安县| 安化县| 夹江县| 思茅市| 镇远县| 咸宁市| 阳城县| 剑川县| 疏附县| 曲沃县| 太原市| 凤阳县| 中牟县|