有時候,在開發中,需要遇到拖拽上傳圖片的需求,即從磁盤選中一張或多張圖片,然后按着鼠標把圖片拖動到頁面上指定的區域,實現圖片的上傳。
需要購買阿里雲產品和服務的,點擊此鏈接領取優惠券紅包,優惠購買哦,領取后一個月內有效: https://promotion.aliyun.com/ntms/yunparter/invite.html?userCode=fp9ccf07
1、后端上傳圖片的接口
我是之前用vue寫一個簡單的后台系統的時候,用Java的SpringMVC+MyBatis的框架寫了一個簡單的后台管理的一些接口,剛好有一個上傳用戶頭像的接口,該接口是把上傳后的圖片存儲在另外一台Tomcat下,這里就直接使用這個接口來上傳圖片。
/** * 上傳用戶頭像 * @param request * @param response */ @ResponseBody @RequestMapping(value="uploadSysHeadImg.do", method=RequestMethod.POST) public void uploadSysHeadImg(HttpServletRequest request,HttpServletResponse response){ JSONObject jo = new JSONObject(); //校驗token // boolean f = tokenService.checkToken(request, response); // if(!f){ return; } try { MultipartResolver resolver = new CommonsMultipartResolver(request.getSession().getServletContext()); MultipartHttpServletRequest Murequest = resolver.resolveMultipart(request); Map<String, MultipartFile> files = Murequest.getFileMap();//得到文件map對象 // 實例化一個jersey Client client = new Client(); List<String> fileNameList = new ArrayList<>(); List<String> relaPathList = new ArrayList<>(); List<String> realPathList = new ArrayList<>(); for(MultipartFile pic: files.values()){ String uploadInfo = Upload.upload(client, pic, request, response, uploadHost, headImgPath); if(!"".equals(uploadInfo)){ //如果上傳成功 String[] infoList = uploadInfo.split(";"); fileNameList.add(infoList[0]); //文件名 relaPathList.add(infoList[1]); //相對路徑 realPathList.add(infoList[2]); //真實路徑 }else{ //如果上傳失敗 fileNameList.add(""); relaPathList.add(""); realPathList.add(""); } } jo.put("success", 1); jo.put("error", null); jo.put("fileNameList", fileNameList); jo.put("relaPathList", relaPathList); jo.put("realPathList", realPathList); }catch (Exception e) { jo.put("success", 0); jo.put("error", "上傳失敗"); } ResponseUtils.renderJson(response, jo.toString()); }
2、前端代碼
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script src="https://cdn.bootcss.com/jquery/1.12.0/jquery.js"></script> <style> #drop_area { position: relative; width: 300px; height: 150px; border: 1px dashed #ddd; border-radius: 5px; margin-bottom: 5px; } #drop_area .text { position: absolute; width: 100%; top: 60px; text-align: center; font-size: 14px; } #img_area { width: 300px; } #img_area img { width: 300px; margin-bottom: 5px; } </style> </head> <body> <div id="drop_area"> <div class="text"><span>+</span><span>將文件拖到此處,即可上傳</span></div> </div> <div id="img_area"></div> </body> <script> var dp = document.getElementById('drop_area'); dp.addEventListener('dragover', function(e) { e.stopPropagation(); //阻止瀏覽器默認打開文件的操作 e.preventDefault(); e.dataTransfer.dropEffect = 'copy'; }); //單圖上傳 // dp.addEventListener("drop", function(e) { // e.stopPropagation(); // //阻止瀏覽器默認打開文件的操作 // e.preventDefault(); // var files = e.dataTransfer.files; // var file = files[0]; // var formData = new FormData(); // formData.append("file", file); // $.ajax({ // type: 'post', // url: 'http://127.0.0.1:8081/ssm_project/sysUser/uploadSysHeadImg.do', // data: formData, // contentType: false, //必須 禁止jQuery設置Content-Type請求頭 // processData: false, //必須 禁止jQuery處理發送的數據 // dataType: "json", // success: function(res){ // if(res.success == 1){ // } // }, // }); // }); //多圖上傳 dp.addEventListener("drop", function(e) { e.stopPropagation(); //阻止瀏覽器默認打開文件的操作 e.preventDefault(); var files = e.dataTransfer.files; var formData = new FormData(); for(var i =0; i<files.length; i++){ formData.append("file"+i, files[i]); } $.ajax({ type: 'post', url: 'http://127.0.0.1:8081/ssm_project/sysUser/uploadSysHeadImg.do', data: formData, contentType: false, //必須 禁止jQuery設置Content-Type請求頭 processData: false, //必須 禁止jQuery處理發送的數據 dataType: "json", success: function(res){ if(res.success == 1){ res.realPathList.forEach(function(item){ $('#img_area').append('<img src="'+item+'">'); }); } }, }); }); </script> </html>
我在這里用的是jquery的ajax請求。
里面用的formData對象來上傳圖片的,該對象的作用是:
1、用一些鍵值對來模擬一系列表單控件,即把form中所有表單元素的name與value組裝成
一個queryString;
2、異步上傳二進制文件
。
另外有兩個屬性的值,我們必須要設置它們的值為false:
contentType: false, //必須 禁止jQuery設置Content-Type請求頭
processData: false, //必須 禁止jQuery處理發送的數據
其中先封裝了一個 formData 對象,然后使用 post 方法將文件傳給服務器。
這里我們就要先說說在 http 中傳輸文件的問題。起初http協議中沒有上傳文件方面的功能,直到rfc1867為http協議添加了這個功能。當然在rfc1867中限定form的method必須為POST, enctype = “multipart/form-data”
以及<input type = "file">
。
當我們使用表單上傳文件時,我們來查看他的Request headers,如下圖:
發現在 multipart/form-data 后面有boundary以及一串字符,這是分界符,后面的一堆字符串是隨機生成的,目的是防止上傳文件中出現分界符導致服務器無法正確識別文件起始位置。說到這肯定就要說說這分界符有啥作用呢?
因為對於上傳文件,我們沒有在使用原有的 http 協議,所以 multipart/form-data 請求是基於 http 原有的請求方式 post 而來的.那么來說說這個全新的請求方式與 post 的區別
-
請求頭的不同,對於上傳文件的請求,
contentType = multipart/form-data
是必須的,而 post 則不是,畢竟 post 又不是只上傳文件~。 -
請求體不同。這里的不同也就是指前者在發送的每個字段內容之間必須要使用分界符來隔開,比如文件的內容和文本的內容就需要分隔開,不然服務器就沒有辦法正常的解析文件,而后者 post 當然就沒有分界符直接以 name = "value"的形似發送。
而在我的這段JQuery ajax() 方法中,我設置了contentType = false
,這不是沖突了嗎?這當然沒有,因為當我們查看這時的 Request headers,會發現還是有分界符。這是因為當我們在 form 標簽中設置了enctype = “multipart/form-data”,
這樣請求中的 contentType 就會默認為 multipart/form-data (我用的是new formData()對象,它其實就是模擬了一個表單控件,也就是form標簽)。而我們在 ajax 中 contentType 設置為 false 是為了避免 JQuery 對其操作,從而失去分界符,而使服務器不能正常解析文件。
3、效果
需要購買阿里雲產品和服務的,可以點擊此鏈接領取優惠券紅包,優惠購買哦,領取后一個月內有效: https://promotion.aliyun.com/ntms/yunparter/invite.html?userCode=fp9ccf07