有时候,在开发中,需要遇到拖拽上传图片的需求,即从磁盘选中一张或多张图片,然后按着鼠标把图片拖动到页面上指定的区域,实现图片的上传。
需要购买阿里云产品和服务的,点击此链接领取优惠券红包,优惠购买哦,领取后一个月内有效: 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); Mapfiles = Murequest.getFileMap();//得到文件map对象 // 实例化一个jersey Client client = new Client(); List fileNameList = new ArrayList<>(); List relaPathList = new ArrayList<>(); List 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>Documenttitle>
<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('
'+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”
以及。
当我们使用表单上传文件时,我们来查看他的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