Web后端—带文件和文本数据的form表单,使用Ajax的FormData获取表单数据上传到servlet,后端调用写的工具类FormPostUtil接收文件文本数据,最后存储到数据库中。

使用commons-fileupload-1.4-bin.zip中的工具来获取带文件流的form表单数据。form表单含文件上传的方法二使用FileItem类 、ServletFileUpload 类、DiskFileItemFactory类实现。(方法一是使用servlet的Part接口实现,在我另一篇文章)

问题分析:

当前端form表单传数据到后端时,会有一个非常棘手的问题:数据里面有没有文件数据呢??因为有与没有的处理方式是不一样的。现在就设计前端使用Ajax申请技术,后端接收请求的同时自动判断form表单数据有没有文件数据,如果有文件数据就就要处理文件流保存到指定路径。

那么如何去判断form表单传来的数据有没有包含文件流呢?这里使用的思想和工具是FileItem类 、ServletFileUpload 类、DiskFileItemFactory类。

使用这三个工具就可以把前端form表单中的所有数据分成一个一个的FileItem对象,每一个数据就是一个对象,这样我们就可以使用循环来判断每一个数据是不是文件数据,这里直接使用if条件语句实现,如果是文件数据就要保存到路径下,如果不是文件数据就使用函数fileItem.getFieldName()把name的名字保存到数据库语句sql中的字段名字符串strfiestr1中;使用fileItem.getString("utf-8")把数据保存到数据库语句sql中的value中,该字符串我取名为strvalSTR1。这是为后面模块化插入数据到数据库做准备的参数。也就是,每取一个就把fileItem.getFieldName()值作为sql命令串的字段名,把fileItem.getString的值作为sql命令串的value内值。


FileItem类

主要用于处理前端传来的数据

ServletFileUpload 类

主要用于判断前端的数据是不是mime协议的方式传过来的

DiskFileItemFactory类

主要用于设置限制上传文件的大小

联合ServletFileUpload生成文件工厂,并保存到FileItem类型的List中,这样就可以逐条数据的判断是否是文件数据了。

DiskFileItemFactory factory1=new DiskFileItemFactory();//来源于:commons-fileupload-1.4.jar
factory1.setSizeThreshold(1024*1024*20);//设置内存中用字节数
ServletFileUpload uploadf1=new ServletFileUpload(factory1); //创建文件工厂
uploadf1.setHeaderEncoding("utf-8");//只能解决文件名或路径中汉字乱码	   
java.util.List<FileItem> fileItemList;	 
fileItemList = uploadf1.parseRequest(request);
for(FileItem fileItem :fileItemList ){
    //循环每一个数据对象,也就是每一个input标签数据
}

一、 FileItem类最重要的五个方法

fileItem.isFormField()方法用于判断FileItem类对象封装的数据是一个普通文本表单字段,还是一个文件表单字段,如果是普通表单字段则返回true,否则返回false。

fileItem.getName()方法用于获得上传文件的文件路径名,后面要处理名字。

fileItem.getFieldName方法用于获取input标签的name值。

fileItem.getString()方法用于获取当前对象的数据值,即input标签中用户输入的内容。

fileItem.writer()方法用于把上传文件写入磁盘中。

二、ServletFileUpload 类

ServletFileUpload.isMultipartContent(request)方法用于判断前端传来的数据是不是mime协议,也就是判断这次所传的数据是不是含有文件数据。enctype="multipart/form-data"

三、DiskFileItemFactory类

factory1.setSizeThreshold(1024*1024*20);//设置内存中用字节数

四、综合以上技术,可自动化实现前端form表单数据不管有没有包含文件数据都可以保存到数据库。

工具类代码:

package com.jdbc.utils;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.sql.SQLException;
import java.sql.Statement;
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;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.mysql.jdbc.Connection;
public class FormPostUtil {
	public void getparm(HttpServletRequest request,HttpServletResponse response, String path1, JSONArray jsonarr1,JSONObject jsonob1,String tablname1) {
		 String fname1=""; //预留文件名,不管以后用不用,如果有文件上传,它就有值;不传文件,它就是空值
		//如果是MIME才能进行文件上传
		 try {
			request.setCharacterEncoding("UTF-8");  
		} catch (UnsupportedEncodingException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		  response.setContentType("text/html;Charset=UTF-8");
		 String sqlfiestr1="";这个串用于保存sql的insert命令中的字段列名,一键搞定
		  String sqlvalSTR1=""; //这个串用于保存sql的insert命令中的vlaue值 
		 DiskFileItemFactory factory1=new DiskFileItemFactory();//来源于:commons-fileupload-1.4.jar
		 factory1.setSizeThreshold(1024*1024*20);//设置内存中用字节数
		 ServletFileUpload uploadf1=new ServletFileUpload(factory1); //创建文件工厂
		 uploadf1.setHeaderEncoding("utf-8");//只能解决文件名或路径中汉字乱码	   
		 java.util.List<FileItem> fileItemList;	 
		 try {
			fileItemList = uploadf1.parseRequest(request);
			
			for(FileItem fileItem :fileItemList )
			 {if(fileItem.isFormField())  //表示是普通参数,不是文件.下面为拼凑sql命令insert串做准备
			   {  sqlfiestr1=sqlfiestr1+fileItem.getFieldName()+",";
			      try {
					sqlvalSTR1=sqlvalSTR1+"'"+fileItem.getString("utf-8")+"',";
				} catch (UnsupportedEncodingException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}	 //要加编码,否则取出来汉字可能是乱码	   
			 
			   }
			 else //不是fileItem.isFormField()),那下一项就是文件流了(以上程序结构要注意,在表单中要把文件最在最后一项,要不然你就在上面循环调用处理文件保存 独立函数
			 { 	 fname1=fileItem.getName();//提前把文件名取出来,后面保存到数据库时要使用
				 savefile(fileItem, path1);//把上传文件写入保存路径
			 }			 
			 }
			//准备把以上数据插入到数据库中; 调用 inserttable()函数
			inserttable("xs", sqlfiestr1, sqlvalSTR1, fname1,fileinputname);//向指定表,插入指定字段名,指定值,是否带文件(如果fname1为空则在表中插入空串即可
		    PrintWriter out1;
			try {
				out1 = response.getWriter();
				out1.println(jsonob1);
				out1.flush();
				out1.close();
			} catch (IOException e) {
				e.printStackTrace();
			}	
		} catch (FileUploadException e) {
			e.printStackTrace();
		} 
	}
	//模块化文件保存
	private void savefile(FileItem fileItem,String path1) {
	    	 String  fname1; 
		     fname1=fileItem.getName();
			 System.out.println(fileItem.getSize());
			 if(fileItem.getSize()>1024*1024*20)  {return;  } //文件太大了,拒绝上传
			
//那说明没有上传文件过来,这样处理 好处是,用户可传可不传文件,无所谓
			 if(fname1==null||fname1.equals("")||fileItem.getSize()==0)
			 {			  }
			 else {
				File savefile1=new File(path1,fname1);
				try {
//可以使用系统时间数字避免文件名重复(我另一篇文件上传的方法中有)
					fileItem.write(savefile1);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		
	}
	private  void  inserttable(String tablename, String sqlfiestr1,String  sqlvalSTR1,String fname1 ,String fileinputname) {
		if(sqlfiestr1.length()<1||sqlvalSTR1.length()<1) //那说明没有数据上传
			;
		else { //说明表单中有数据上传 ,这样才有必要写到数据库中。 同学们,这种偷懒 方法前提是HMTL表单中的控件name要与数据库表列名相同才行。
		String sqlstr1="";
		if(fname1==""||fname1.equals(""))//说明没有上传文件
		{	//要去掉字段名串最后多的一个逗号
			sqlfiestr1=sqlfiestr1.substring(0,sqlfiestr1.length()-1);
			sqlvalSTR1=sqlvalSTR1.substring(0,sqlvalSTR1.length()-1);
			sqlstr1="insert into "+tablename+"("+sqlfiestr1+")values("+sqlvalSTR1+")";
		 
		}
		else //表示有文件上来,文件名不为空 .调整insert串照片参数
		{  sqlfiestr1=sqlfiestr1+ fileinputname ;//fileinputname是文件控件的name的属性值也是数据库中的字段名。
		  int po=fname1.lastIndexOf("\\"); //只保留后面的文件名,不要前面路径
		 if(po!=-1)//说明找到了右斜杠\
		 { fname1=fname1.substring(po+1);}
		  sqlvalSTR1=sqlvalSTR1+"'"+fname1+"'";  
		   sqlstr1="insert into "+tablename+"("+sqlfiestr1+")values("+sqlvalSTR1+")";
		}
		   
		System.out.println(sqlstr1);
		Connection con1=(Connection) JdbcConUtil.getConnection();
		Statement st1;
		try {
			st1 = con1.createStatement();
			  st1.execute(sqlstr1);
			  JdbcConUtil.close(st1, con1);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		}
	}
	
	
	
}

需要使用alibaba的json的jar包、commons-fileupload的jar包、commons-io的jar包

alibaba的json的jarhttps://pan.baidu.com/s/1-xj4pgv61_D5v637FKMd4g?pwd=6666%C2%A0

commons-iohttps://pan.baidu.com/s/14vv4G5InhyO9OtwZxxwDqg?pwd=6666%C2%A0

commons-fileuploadhttps://pan.baidu.com/s/1pjVKYmID4edb_mdggMeZzw?pwd=6666%C2%A0


servlet测试代码:

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException 
	{  
      JSONArray jsonarr1=new JSONArray();
	  JSONObject jsonob1=new JSONObject();	 
	  String path1="C:\\JAVA3\\uploadfile";	 //指定保存上传文件的服务器位置
	  String tablname1="xs";//指定要操作的表名
	 if(ServletFileUpload.isMultipartContent(request))//判断是不是mime方式过来的
	  { 
            new FormPostUtil.getparm(request, response, path1, jsonarr1, jsonob1,tablname1);		
	    }
	else { 
            //这里处理不带文件的数据,这里是自己的另一个插入数据的工具类InsertObject方法
          BeanUtils.populate(tempCourse, request.getParameterMap());
          String sql1 = "insert into course(cno,cname,tno) values(?,?,?)";
		  JdbcCRUDUtil.InsertObject(sql1, tempCourse);
	}



经过一番思考:决定把上面的文件上传功能更加完善。

完善点如下:

1.实现多文件的上传

2.实现文件控件在form表单中的任意位置

(上面的代码文件控件只能在form表单的最后面,有局限性)

3.inserttable()函数的参数减少了,只需要:表名、sql字段名、sql的values值

但是:该方法必须要在数据库中设置多个字段用于文件存储,也就是前端中每一个文件控件都必须对应数据库中每一个字段。所以还需要改进。

改进:数据库中只设置一个字段用于保存前端所有文件的名字,文件名之间以特殊符间隔。但是以后取照片时也需要以这个间隔符来一个一个取照片。

工具类FormPostInsertUtil源代码:

/*
*  从前端接收数据插入到数据库一步到位工具类
*  servlet测试代码:
*  String path1="D:\\Web后端开发技术\\uploadFile";	 //指定保存上传文件的服务器位置
*  String tablname1="user";//指定要操作的数据库表名
*  new FormPostInsertUtil().getParmInsert(request, response, path1, tablname1);
* */
package com.jdbc.utils;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.sql.SQLException;
import java.sql.Statement;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.sf.json.JSONObject;
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;
import com.alibaba.fastjson.JSONArray;
import com.mysql.jdbc.Connection;

public class FormPostInsertUtil {
	public  void getParmInsert(HttpServletRequest request,HttpServletResponse response, String path1,String tablname1) {
		System.out.println("进入servlet");
		try {
			request.setCharacterEncoding("UTF-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		response.setContentType("text/html;Charset=UTF-8");
		String sqlfiestr1=""; //这个串用于保存sql的insert命令中的字段列名,一键搞定
		String sqlvalSTR1=""; //这个串用于保存sql的insert命令中的vlaue值
		DiskFileItemFactory factory1=new DiskFileItemFactory();//来源于:commons-fileupload-1.4.jar
		factory1.setSizeThreshold(1024*1024*20);//设置内存中用字节数
		ServletFileUpload uploadf1=new ServletFileUpload(factory1); //创建文件工厂
		uploadf1.setHeaderEncoding("utf-8");//只能解决文件名或路径中汉字乱码
		java.util.List<FileItem> fileItemList;
		try {
			fileItemList = uploadf1.parseRequest(request);
			for(FileItem fileItem :fileItemList )
			{
				if(fileItem.isFormField())  //表示是普通参数,不是文件.下面为拼凑sql命令insert串做准备
				{
					sqlfiestr1=sqlfiestr1+fileItem.getFieldName()+",";//fileItem.getFieldName()获取input标签的name名字
					System.out.println(fileItem.getFieldName());
					try {
						sqlvalSTR1=sqlvalSTR1+"'"+fileItem.getString("utf-8")+"',";//fileItem.getString("utf-8")获取值
						System.out.println(fileItem.getString("utf-8"));
					} catch (UnsupportedEncodingException e) {
						e.printStackTrace();
					}	 //要加编码,否则取出来汉字可能是乱码
				}

				 /*fileItem.getName()有些浏览器可以直接获取到文件名,不需要处理
				 但是如果有的浏览器获取的是带有路径的名字,这里要处理后才可以拼接fileinputnames*/

				else //当前项是文件流,要把数据写入磁盘
				{
					String tempFileinputname=fileItem.getFieldName();//文件标签名
					String tempFilename=fileItem.getName();//文件名

					//处理文件名字
					int po=tempFilename.lastIndexOf("\\"); //只保留后面的文件名,不要前面路径
					if(po!=-1)//说明找到了右斜杠\
					{ tempFilename=tempFilename.substring(po+1);}
					//处理文件名完毕

					if(tempFilename==""||tempFilename.equals(""))//如果没有上传文件,什么也不做
					{  }
					//有上传文件就添加sql语句,并写入磁盘
					else{
						sqlfiestr1=sqlfiestr1+tempFileinputname+",";//fileItem.getFieldName()获取input标签的name名字
						sqlvalSTR1=sqlvalSTR1+"'"+tempFilename+"',";//所有上传文件的名字
						savefile(fileItem, path1);//把上传文件写入磁盘中,即保存上传文件
					}
				}
			}
			//准备把以上数据插入到数据库中; 调用 inserttable()函数
			inserttable(tablname1, sqlfiestr1, sqlvalSTR1);//向指定表,插入指定字段名,指定值,上传文件字段名,上传文件名字

		/*
		* 这里的jsonob1对象看似没有任何作用,但是如果前端ajax的接收数据类型为dataType: "json",时
		* 若没有这里的输出,ajax会到error函数,并不是到success函数,所以必须给前端输出一个json对象
		*/
			JSONObject jsonob1=new JSONObject();
			PrintWriter out1;
			try {
				out1 = response.getWriter();
				out1.println(jsonob1);
				out1.flush();
				out1.close();
			} catch (IOException e) {
				e.printStackTrace();
			}

		} catch (FileUploadException e) {
			e.printStackTrace();
		}
	}
	/*
	 * 关于fileItem.getName()获取文件名字有个注意事项
	 * 网上说有的浏览器获取的直接就是文件的名字
	 * 而有的浏览器获取的是带有路径的文件名字
	 * 为了两者兼容,直接使用处理带路径的文件名字的方法
	 * */
	private void savefile(FileItem fileItem,String path1) {
		System.out.println("来了老弟,要保存文件了");
		String  fname1;
		fname1=fileItem.getName();
		//去掉前面的路径,只要后面干净文件名。
		int po=fname1.lastIndexOf("\\");
		if(po!=-1)//说明找到了右斜杠\
		{fname1=fname1.substring(po+1);}
		if(fileItem.getSize()>1024*1024*20) {return;  } //文件太大了,拒绝上传
		if(fname1==null||fname1.equals("")||fileItem.getSize()==0)
		{  }//那说明没有上传文件过来,这样处理 好处是,用户可传可不传文件,无所谓
		else {
			File savefile1=new File(path1,fname1);
			try {
				fileItem.write(savefile1);//可以使用系统时间数字避免文件名重复(我另一篇文件上传的方法中有)
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	/*
	 * 参数:
	 * tablename:表名
	 * sqlfiestr1:用于保存sql的insert命令中的字段列名
	 * sqlvalSTR1:用于保存sql的insert命令中的vlaue值
	 */
	private  void  inserttable(String tablename, String sqlfiestr1, String sqlvalSTR1) {
		System.out.println("开始保存数据到数据库");
		if(sqlfiestr1.length()<1||sqlvalSTR1.length()<1) //那说明前端没有数据上传
		{   }
		else { //说明表单中有数据上传 ,这样才有必要写到数据库中。这种偷懒 方法前提是HMTL表单中的控件name要与数据库表列名相同才行。
			String sqlstr1="";
			//要去掉字段名串最后多的一个逗号
			sqlfiestr1=sqlfiestr1.substring(0,sqlfiestr1.length()-1);
			sqlvalSTR1=sqlvalSTR1.substring(0,sqlvalSTR1.length()-1);
			sqlstr1="insert into "+tablename+"("+sqlfiestr1+") values("+sqlvalSTR1+")";
			System.out.println(sqlstr1);
			Connection con1=(Connection) JdbcConUtil.getConnection();
			Statement st1;
			try {
				st1 = con1.createStatement();
				st1.execute(sqlstr1);
				JdbcConUtil.close(st1, con1);
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
}

servlet源代码:

package com.servlet;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.jdbc.utils.FormPostUtil;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;

@WebServlet(name = "regionServlet", value = "/regionServlet")
public class regionServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String path1="D:\\Web后端开发技术\\uploadFile";	 //指定保存上传文件的服务器位置
        String tablname1="user";//指定要操作的数据库表名
        new FormPostUtil().getparm(request, response, path1, tablname1);
    }
}

HTML源代码:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>用户注册</title>
<script src="./jquery-3.4.1.min.js"></script>
</head>
<body>
用户注册<br>
	<form action="regionServlet" method="POST" id="form1" enctype="multipart/form-data">
		用户名:<input type="text" name="username" id="username"><br>
		头像2:<input type="file" name="avatar2" id="tx2"><br>
		密码:<input type="password" name="password" id="password"><br>
		头像1:<input type="file" name="avatar" id="tx"><br>

	</form>
	<button id="regionBtn" >注册</button>

	<script>
		$(document).ready(function() {
			  $("#regionBtn").click(function () {
					var url0=$("#form1").attr("action");
					alert(url0);
					// var data0=$("#form1").serialize();
				    var data0=new FormData($("#form1")[0]);
					alert(data0);

					$.ajax({
						type:"post",
						url:url0,
						cache:false,
						data:data0,
						processData:false,
						contentType:false,
						dataType:"json",
						success:function(re)
						{
							alert("ok");
						},
						error: function(re)
						{
							alert("no");
						}
					})
			  })
		})
	</script>
</body>
</html>


还有另外一个上传文件的方法,使用servlet的Part接口实现:

https://blog.csdn.net/weixin_45947938/article/details/124295211https://blog.csdn.net/weixin_45947938/article/details/124295211

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值