SpringMVC Ajax上传文件实例

做了一个文件上传模块,因为传统的form提交会有页面刷新,不符合我的使用要求,所以我采用Ajax提交方式,这里说明下,我的应用程序前端为Ajax提交,后端SpringMVC接收处理。

传统form提交文件方式:

<form id="uploadPic" action="/user/saveHeaderPic" method="post" enctype="multipart/form-data">
    <input type="file" name="file"/>
    <input type="submit" value="提交"/>
</form>

采用Ajax提交文件,我先后出现了如下两个问题:

Ajax post 400 (Bad Request)

HTTP Status 400 - Required CommonsMultipartFile parameter ‘pic’ is not present

这里先解释下错误原因:

问题1:

Ajax参数错误导致,上传文件的form我使用了jquery.form.js的form序列化,这样传输表单到后台很方便,但是二进制文件是无法用form.serialize()序列化的。

所以最终我采用了FormData的传输方式,XMLHttpRequest Level 2添加了一个新的接口FormData。利用FormData对象,我们可以通过JavaScript用一些键值对来模拟一系列表单控件,我们还可以使用XMLHttpRequest的send()方法来异步的提交这个“表单”。比起普通的ajax,使用FormData的最大优点就是我们可以异步上传一个二进制文件。

但使用formData对浏览器有一定要求(Chrome 7+、Firefox 4+、IE 10+、Opera 12+、Safari 5+),如果程序需要兼容低版本浏览器,可去查看其他JS上传控件或flash上传控件。

formData使用参看:https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/FormData

问题2:

这个问题是因为不细心导致的 - -,后端接收的参数名和前端file控件name名称不一致导致。

好啦,下面给出我的前后端代码示例:

HTML:

<form id="uploadPic" action="#" enctype="multipart/form-data">
    <input type="file" name="file">
    <a href="javascript:savePic();" class="btn green"> 提交 </a>
</form>

JS脚本:

<script type="text/javascript">
    function savePic(){
        var formData = new FormData($( "#uploadPic" )[0]);  
        var ajaxUrl = "${path}/rest/user/saveHeaderPic";
        //alert(ajaxUrl);
        //$('#uploadPic').serialize() 无法序列化二进制文件,这里采用formData上传
        //需要浏览器支持:Chrome 7+、Firefox 4+、IE 10+、Opera 12+、Safari 5+。
        $.ajax({
            type: "POST",
            //dataType: "text",
            url: ajaxUrl,
            data: formData,
            async: false,  
            cache: false,  
            contentType: false,  
            processData: false,
            success: function (data) {
            alert(data);
            },
            error: function(data) {
                alert("error:"+data.responseText);

             }
        });
        return false;
    }

JAVA后端:

/**
 * 头像图片上传
 * @throws IOException 
 */
@RequestMapping(value = "/saveHeaderPic", method = RequestMethod.POST)
public void saveHeaderPic(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request, HttpServletResponse response) throws IOException {

        String resMsg = "";
    try {

        long  startTime=System.currentTimeMillis();

        System.out.println("fileName:"+file.getOriginalFilename());
        String path="/Users/loukai/easylife/files/"+new Date().getTime()+file.getOriginalFilename();
        System.out.println("path:" + path);

        File newFile=new File(path);
        //通过CommonsMultipartFile的方法直接写文件
        file.transferTo(newFile);
        long  endTime=System.currentTimeMillis();
        System.out.println("运行时间:"+String.valueOf(endTime-startTime)+"ms");
        resMsg = "1";
    } catch (IllegalStateException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        resMsg = "0";
    }
          response.getWriter().write(resMsg);

      }

运行测试,文件上传成功!

第二部份:

<input type="file" multiple="true" id="file" name="file"/><button class="u-button u-button-primary" onclick="uploadFileMethod1()" id="doUpload">确认附件</button>

js代码

单个文件上传

function uploadFileMethodSingleFile()
  {
	 
      var FileController = $ctx+"/fankuiController/saveFiles";                    // 接收上传文件的后台地址
      // FormData 对象
      var fileName = document.getElementById("file").value;
     // alert(getPath(fileObj));
      var form = new FormData();
 
      var fileObj = document.getElementById("file").files[0]; // 获取文件对象
       form.append("file ",fileObj );   // 文件对象  
 
      var xhr = new XMLHttpRequest();
      xhr.open("post", FileController, true);
      xhr.send(form);
      xhr.onload = function () {
          alert("上传完成!");
      };
  }

单个文件上传时,controller用MultipartFile 类型的参数接受文件

@RequestMapping(method = RequestMethod.POST,value = "saveFiles")
	public @ResponseBody Map<String,Object> saveFiles(@RequestParam(value = "file") MultipartFile file, HttpServletRequest request) {
		Map<String,Object> reg=new HashMap<String,Object>();  
		//将内存中的数据写入磁盘
		System.out.println("开始进行附件上传");
		
		try {
				//String filePath = request.getSession().getServletContext().getRealPath("/");
				String filePath ="c:\\";
				MultipartFile file = fileArray[i];
				String originalFileName = file.getOriginalFilename();
				String newFileName = UUID.randomUUID()+originalFileName;
				String finalPath = filePath+newFileName;
				System.out.println(originalFileName);
				System.out.println(newFileName);
				System.out.println(finalPath);
				System.out.println("参数"+request.getParameter("json_filesNameArray"));
				System.out.println("file"+file.getContentType());
				
				File fileAttach = new File(finalPath);
				file.transferTo(fileAttach);
			
		} catch (Exception e1) {
			e1.printStackTrace();
		}
		//上传成功后,将附件加入数据库附件表的blob字段中
		
		
	          
		  reg.put("result","success");
		  return reg;
	}

如果是多个文件上传 fomdata 中要使用formdata.append() 对多个文件进行遍历 然后进行上传

function uploadFileMethod1()
  {
	 
      var FileController = $ctx+"/fankuiController/saveFiles";                    // 接收上传文件的后台地址
      // FormData 对象
      var fileName = document.getElementById("file").value;
     // alert(getPath(fileObj));
      var form = new FormData();
      form.append("json_filesNameArray", fileName);                        // 可以增加表单数据
//      var fileArray = new Array();
//      for(var i=0;i<fileArray.length;i++)
//    {
//    	  var fileObj = document.getElementById("file").files[i]; // 获取文件对象
//    	  fileArray.push(fileObj);
//    }
      var files = document.getElementById("file").files;
      for(var i=0; i< files.length; i++){
    	  alert(files[i].name);
    	  form.append("fileArray",files[i]);   // 文件对象  
    	  } 
      // XMLHttpRequest 对象
      //var fileObj = document.getElementById("file").files[0];
      //form.append("fileArray", fileObj);
      var xhr = new XMLHttpRequest();
      xhr.open("post", FileController, true);
      xhr.send(form);
      xhr.onload = function () {
          alert("上传完成!");
      };
  }

多文件上传时,controller端,进行接收时 使用 MultipartFile[] 类型参数 接受 数组类型的多个文件,然后遍历数组进行操作

@RequestMapping(method = RequestMethod.POST,value = "saveFiles")
	public @ResponseBody Map<String,Object> saveFiles(@RequestParam(value = "fileArray") MultipartFile[] fileArray, HttpServletRequest request) {
		Map<String,Object> reg=new HashMap<String,Object>();  
		//将内存中的数据写入磁盘
		System.out.println("开始进行附件上传");
		System.out.println(fileArray.length);
		try {
			for(int i=0;i<fileArray.length;i++)
			{
				//String filePath = request.getSession().getServletContext().getRealPath("/");
				String filePath ="c:\\";
				MultipartFile file = fileArray[i];
				String originalFileName = file.getOriginalFilename();
				String newFileName = UUID.randomUUID()+originalFileName;
				String finalPath = filePath+newFileName;
				System.out.println(originalFileName);
				System.out.println(newFileName);
				System.out.println(finalPath);
				System.out.println("参数"+request.getParameter("json_filesNameArray"));
				System.out.println("file"+file.getContentType());
				
				File fileAttach = new File(finalPath);
				file.transferTo(fileAttach);
			}
			
		} catch (Exception e1) {
			e1.printStackTrace();
		}

第三部份:

页面表单:

<form id="frm_identityA" action="" enctype="multipart/form-data">
	<span style="display:none">
		<input type="file" id="identityA" name="identityA" value="">
	</span>
	<input type="hidden" name="mobile" value="***********">
</form>

页面图片标签和按钮:

<img id="img_identityA" src="app/images/icon011.png" alt="" name="img_identityA"/>
<button type="button" id="button_identityA" class="btn btn-primary btn-lg btn-block">点击上传图片</button>

页面操作:

// 点击按钮的时候选择图片
$("#button_identityA").click(function(){
	$("#identityA").click();
});
// input框改变的时候将图片发送给后台
$("#identityA").change(function() {
	var formData = new FormData($("#frm_identityA")[0]);
	$.ajax({
		url : "userregeste/file/upload.do", // 自行按需配置好完整的url,包括ip和端口号
		type : "POST",
		data : formData,
		async : false,
		cache : false,
		contentType : false,
		processData : false,
		success : function(returndata) {
			alert("success");
			$("#img_identityA").attr("src","userregeste/file/showImages.do?mobile=***********&name=identityA&"+new Date().toTimeString());
			$("#img_identityA").attr("width","124");
			$("#img_identityA").attr("height","124");
		},
		error : function(returndata) {
			alert("error");
			$("#img_identityA").attr("src","app/images/icon011.png");
		}
	});
});

Spring配置:

<!-- 图片获取 maxUploadSize:设置最大限制 字节为单位-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
	<property name="maxUploadSize" value="1024000"></property>
</bean>
<!-- SpringMVC文件上传 -->
    <bean id="multipartResolver"
        class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!--defaultEncoding:请求的编码格式必须和用户JSP的编码一致,以便正确读取表单中的内容。 
            uploadTempDir:文件上传过程中的临时目录,上传完成后,临时文件会自动删除 
            maxUploadSize:设置文件上传大小上限(单位为字节) -->
        <property name="defaultEncoding" value="UTF-8" />
        <property name="maxUploadSize" value="102400000" />
        <!-- uploadTempDir可以不做设置,有默认的路径,上传完毕会临时文件会自动被清理掉 -->
        <property name="uploadTempDir" value="upload/temp"></property>
    </bean>

1.defaultEncoding:表示用来解析request请求的默认编码格式,当没有指定的时候根据Servlet规范会使用默认值 ISO-8859-1 。当request自己指明了它的编码格式的时候就会忽略这里指定的defaultEncoding。

2.uploadTempDir:设置上传文件时的临时目录,默认是Servlet容器的临时目录。

3.maxUploadSize:设置允许上传的最大文件大小,以字节为单位计算。当设为-1时表示无限制,默认是-1。

4.maxInMemorySize:设置在文件上传时允许写到内存中的最大值,以字节为单位计算,默认是10240。

Action中的代码:
1、图片上传

@Controller
@RequestMapping("/userregeste")
public class ImageUpLoadAction {
	
	/**
	 * 存放上传的图片信息
	 */
	private static Map<String,byte[]> images;
	
	static {
		images = new HashMap<String, byte[]>();
	}
	
	@RequestMapping("/file/upload")
	public ProcessResultModel upLoad(HttpServletRequest request,HttpServletResponse response) {
		// 从请求中获取到文件信息需要将请求转换为MultipartHttpServletRequest类型
		MultipartHttpServletRequest MulRequest = request instanceof MultipartHttpServletRequest ? (MultipartHttpServletRequest) request : null;
		request.getParameter("mobile");// 依然可以从请求中获取到除图片之外的参数
		Iterator<String> fileNames = MulRequest.getFileNames();
		if (fileNames.hasNext()) { // 遍历请求中的图片信息
			String fileName = fileNames.next(); // 图片对应的参数名
			log.debug("fileName:" + fileName);
			file = MulRequest.getFile(fileName); // 获取到图片
			if (file != null) {
				log.debug("file.getSize():" + file.getSize()); // 图片大小
				file.getBytes();// 可以获取到图片的字节数组
				images.put(fileName,file.getBytes());// 获取到图片以字节数组形式保存在服务器内存中
			}
		}
	}
}

2、图片显示
Ajax请求发送成功之后的方法中的操作,
$("#img_identityA").attr("src","userregeste/file/showImages.do?mobile=***********&name=identityA&"+new Date().toTimeString());
值得一提的是,img标签中的src属性,对于UI人员来说就是存放静态图片资源的,但是对于程序员来说,应该要知道img标签实际上会根据src属性去发送一次请求。同时,浏览器对于img的src属性的处理方式是如果src属性值不变,只会发送一次请求,所有加上new Date().toTimeString(),使每次的请求都不相同。
代码如下:

@RequestMapping("/file/showImages")
public String showImages(HttpServletRequest request,HttpServletResponse response) throws IOException {
	log.debug("请求地址:"+request.getRequestURI()+",开始获取图片");
	OutputStream sout = null;
	String mobile = request.getParameter("mobile");
	String name = request.getParameter("name"); // 图片名称
	log.debug("mobile:"+mobile+"	name:"+name);
	if (mobile == null || name == null) {
		log.debug("手机号或图片名为空,获取图片失败!");
		return null;
	}
	byte[] pictrue = null;
	// 从本地Map中去获取images图片
	pictrue = images.get(name);
	log.debug("图片大小:"+pictrue.length);
	try {
		if (pictrue != null) {
			response.setContentType("text/html");
			sout = response.getOutputStream();
			sout.write(pictrue);
			sout.flush();
			sout.close();
			sout = null;
		} else {
			return null;
		}
	} catch (Exception e) {
		e.printStackTrace();
	} finally {
		if (sout != null) {
			sout.close();
			sout = null;
		}
	}
	log.debug("返回图片成功!");
	return null;
}

如此,页面上会成功的显示图片。

*需要的jar包除了SpringMVC的所有jar包之外,还需要
commons-fileupload-1.3.1.jar
commons-io.jar
*设置enctype="multipart/form-data"后,表单数据是以二进制形式进行传输的,commons-fileupload-1.3.1.jar 即是帮我们解析二进制数据并且封装到parameter里面,不添加这个包,从HttpServletRequest获取不到参数,可以获取二进制流数据自行解析。
*以上的实现动态的将图片传送到后台,可以在后台对图片进行一系列处理,效率比较高。

页面效果:

猜你喜欢

转载自blog.csdn.net/qmdweb/article/details/83061531
今日推荐