这一篇讲“将选择的图片上传到指定的服务器”。
说实话,这个功能写了好久,一会儿json拼的有问题访问不到后台,一会儿后台又说form表单的enctype不是multipart/form-data类型,各种乱七八糟的问题折腾了一下午都没好,第二天再弄的时候,竟然莫名其妙的好了,而且后来再也没出过问题。真是匪夷所思啊!
好啦,废话说到这里,言归正传。
这里以发布公告为例:输入公告内容,上传需要的图片(也可以不上传),点击“保存”按钮进行数据提交。其中内容能够和图片必须有一项不为空,“保存”按钮触发事件savaAnnounce()。
一、提交数据的代码如下:
/** *该方法用来保存公告 * @param obj:A标签 * **/ function savaAnnounce(obj){ //判断保存是灰色的还是绿色的,若是灰色的不能保存 var opacity = $(obj).css("opacity"); //当前按钮处于置灰状态,不能操作 if((opacity+"")=="0.5") return false; //进行进度条提示 api.showProgress({ title: '数据提交中', modal: false }); //1、获取公告内容 var announceContent = jQuery.trim($("#announceContent").val());//去除左右的空格 var contentLen = announceContent.length; if(contentLen>0){//说明有填写内容,那么校验是否有特殊词汇,若是没有内容不校验 //判断是否有敏感词汇 if(checkStr2(announceContent) || checkStr2(announceContent)){//两个当中有一个有特殊字符 showErrorInfoBySys("内容不允许有特殊字符","1"); return false; } }else{//说明没有数据 announceContent = ""; } //2、获取图片数量 var picLen = $("#picList").children().length; //alert(contentLen+"\n"+picLen); //3、若是内容和图片数量都为0,说明什么都没有填写,那么不能提交数据 if(contentLen<=0 && picLen<=0){ showErrorInfoBySys("请输入内容或上传图片","1"); return false; } //4、处理图片路径信息 var fileList = ""; if(picLen>0){ $("#picList").children().each(function(i){ fileList+=",\"file"+(i+1)+"\":\""+this.children[0].src+"\"";//只显示文件名称 }); //截取掉最开始的逗号 if(fileList.length>1){ fileList = fileList.substring(1); } } fileList = eval('('+("{"+fileList+"}")+')'); //4、提交数据 api.ajax({ url:$api.getStorage("url"), method:'post', dataType:'text', data:{ values:{ 'announceContent':announceContent, 'user_id':$api.getStorage("u_id"), 'user_name':$api.getStorage("u_name"), 'time':new Date().getTime() }, files:fileList } },function(ret, err){ api.hideProgress();//隐藏进度提示框 if (ret) { //跳转到公告列表界面: api.openWin({ name: 'my_notice', url: 'mynotice.html', pageParam:{ 'user_id':$api.getStorage("u_id") }, progress:{ type:"default", title:"玩命加载中..." } }); } else { api.alert(JSON.stringify(err)); } }); } /** *该方法用来显示错误信息-调用系统定时方法 * @param flag:1-隐藏进度提示框 非1:不隐藏 * @param msg:错误信息 * **/ function showErrorInfoBySys(msg,flag){ if((flag+"")=="1"){ api.hideProgress();//隐藏进度提示框 } //显示错误信息 api.toast({ msg: msg, duration: time, location: 'bottom' }); } /**该方法用来判断字符串中是否存在特殊字符-只包括SQL常用关键字 /*true:说明有特殊字符 /*false:说明没有特殊字符 */ function checkStr2(str){ var myReg = new RegExp("(\\b(select|update|and|or|delete|insert|trancate|char|into|substr|ascii|declare|exec|count|master|into|drop|execute)\\b)"); if(myReg.test(str)) return true; return false; }
二、后台进行文件上传:
servlet:
String retuStr="提交失败!";
Object result = new com.wjl.dataengine.UploadFile().upload(request, response);
if(result instanceof SUser ){//如果是user的子类,说明添加成功
//往数据库中添加日志
addLog((SUser)result,"添加公告");
retuStr="提交成功!";
}else{
retuStr = result.toString();//否则就是错误信息
}
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html");
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Cache-Control", "no-store");
response.setDateHeader("Expires", 0);
System.out.println("retuStr:"+retuStr);
response.getWriter().write(retuStr);
response.getWriter().flush();
response.getWriter().close();
文件上传:
/**
* 该方法用来上传文件
* @param request:request请求
* @return Object:若是suer对象说明添加成功,否则返回错误信息
* **/
public Object upload(HttpServletRequest request,final HttpServletResponse response){
Object retuStr="提交失败!";
//1、判断提交上来的数据是否是上传表单的数据
if(!ServletFileUpload.isMultipartContent(request)){//不是上传表单的数据
//按照传统方式获取数据
String announceContent = request.getParameter("announceContent");//公告内容
String user_id = request.getParameter("user_id");//发布人序号
String user_name = request.getParameter("user_name");//发布人
System.out.println("内容:\n"+announceContent);
/*
try{
String announceContent2 =URLDecoder.decode(announceContent,"utf-8");
announceContent = announceContent2;
System.out.println("转码之后的公告内容:"+announceContent);
}catch(Exception e){
e.printStackTrace();
}
*/
if(announceContent==null || announceContent.trim().length()<=0){
return "内容不能为空!";
}else{
result= new com.wjl.ServicesImpl().addAnnounce(announceContent,"",user_id,user_name);
if(result.trim().length()<=0){//说明添加成功了
SUser user = new SUser();
user.setS_user_id(Integer.parseInt(user_id));
user.setS_username(user_name);
return user;
}else{//说明添加失败
return retuStr;
}
}
}
//2、处理上传了文件的情况
//得到上传文件的保存目录,将上传的文件存放于WEB-INF目录下,不允许外界直接访问,保证上传文件的安全
String savePath = getSavePath();
if(savePath.length()<=0){
savePath = request.getRealPath("/")+"upload";
}
String year = com.wjl.Time.year().toString();//年份
String month = com.wjl.util.Time.month().toString();//月份
String day = com.wjl.util.Time.date().toString();//日
String tempPathForFile = year+File.separator+month+File.separator+day;
String filePath = "";
//上传时生成的临时文件保存目录
String tempPath = getTempPath();
if(tempPath.length()<=0){
tempPath = request.getRealPath("/")+"temp";
}
File tmpFile = new File(tempPath);
if (!tmpFile.exists()) {
//创建临时目录
tmpFile.mkdir();
}
try{
//使用Apache文件上传组件处理文件上传步骤:
//1、创建一个DiskFileItemFactory工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
//设置工厂的缓冲区的大小,当上传的文件大小超过缓冲区的大小时,就会生成一个临时文件存放到指定的临时目录当中。
factory.setSizeThreshold(1024*100);//设置缓冲区的大小为100KB,如果不指定,那么缓冲区的大小默认是10KB
//设置上传时生成的临时文件的保存目录
factory.setRepository(tmpFile);
//2、创建一个文件上传解析器
ServletFileUpload upload = new ServletFileUpload(factory);
//监听文件上传进度
upload.setProgressListener(new ProgressListener(){
public void update(long pBytesRead, long pContentLength, int arg2) {
try {
float f = pBytesRead/pContentLength;
response.getWriter().write(f+"");
} catch (IOException e) {
e.printStackTrace();
}
}
});
//解决上传文件名的中文乱码
upload.setHeaderEncoding("UTF-8");
//设置上传单个文件的大小的最大值,目前是设置为10*1024*1024字节,也就是10MB
upload.setFileSizeMax(getFileBufferForSingle());
//设置上传文件总量的最大值,最大值=同时上传的多个文件的大小的最大值的和,目前设置为20MB
upload.setSizeMax(getFileBufferFotTotal());
//4、使用ServletFileUpload解析器解析上传数据,解析结果返回的是一个List<FileItem>集合,每一个FileItem对应一个Form表单的输入项
List<FileItem> list = upload.parseRequest(request);
//获取公告信息
String announceContent = "";//公告内容
String user_id = "";//发布人序号
String user_name = "";//发布人
StringBuffer imgInfo = new StringBuffer();//图片信息
for(FileItem item : list){
//如果fileitem中封装的是普通输入项的数据
if(item.isFormField()){
String name = item.getFieldName().trim();
//解决普通输入项的数据的中文乱码问题
String value = item.getString("UTF-8");
if(value==null || value.trim().length()<=0)value="";
else value = value.trim();
//value = new String(value.getBytes("iso8859-1"),"UTF-8");
//System.out.println("基础信息:\n"+name + "=" + value);
//System.out.println("传过来的参数值:"+FieldType.getFileType(name));
switch(FieldType.getFileType(name)){
case ANNOUNCECONTENT:announceContent = value;break;
case USER_ID:user_id = value;break;
case USER_NAME:user_name = value;break;
case TIME:break;
default:break;
}
}else{//如果fileitem中封装的是上传文件
//得到上传的文件名称,
String filename = item.getName();
if(filename==null || filename.trim().equals("")){
continue;
}
//注意:不同的浏览器提交的文件名是不一样的,有些浏览器提交上来的文件名是带有路径的,如: c:\a\b\1.txt,而有些只是单纯的文件名,如:1.txt
//处理获取到的上传文件的文件名的路径部分,只保留文件名部分
filename = filename.substring(filename.lastIndexOf("\\")+1);
//得到上传文件的扩展名
//String fileExtName = filename.substring(filename.lastIndexOf(".")+1);
//如果需要限制上传的文件类型,那么可以通过文件的扩展名来判断上传的文件类型是否合法
//System.out.println("上传的文件名称是:"+filename+"\n上传的文件的扩展名是:"+fileExtName);
//获取item中的上传文件的输入流
InputStream in = item.getInputStream();
//得到文件保存的名称
String saveFilename = makeFileName(filename);
//得到文件的保存目录
String realSavePath = makePath(saveFilename, tempPathForFile);
//拼接图片路径用来保存到数据库
imgInfo.append(","+File.separator+"bfp"+File.separator+realSavePath+File.separator+saveFilename);
realSavePath = savePath+File.separator+realSavePath;
System.out.println("文件目录:"+realSavePath);
//File既可以代表文件也可以代表目录
File file = new File(realSavePath);
//如果目录不存在
if(!file.exists()){
//创建目录
file.mkdirs();
}
//创建一个文件输出流
FileOutputStream out = new FileOutputStream(realSavePath+File.separator+saveFilename);
//创建一个缓冲区
byte buffer[] = new byte[1024];
//判断输入流中的数据是否已经读完的标识
int len = 0;
//循环将输入流读入到缓冲区当中,(len=in.read(buffer))>0就表示in里面还有数据
while((len=in.read(buffer))>0){
//使用FileOutputStream输出流将缓冲区的数据写入到指定的目录(savePath + "\\" + filename)当中
out.write(buffer, 0, len);
}
//关闭输入流
in.close();
//关闭输出流
out.close();
//删除处理文件上传时生成的临时文件
item.delete();
//retuStr = "文件上传成功!";
}
}
String img = "";
if(imgInfo.length()>1){
img = imgInfo.substring(1);
}else{img="''";}
//往数据库中添加内容
String result="";
result= new com.wjl.ServicesImpl().addAnnounce(announceContent,img,user_id,user_name);
if(result.trim().length()<=0){//说明添加成功了
SUser user = new SUser();
user.setS_user_id(Integer.parseInt(user_id));
user.setS_username(user_name);
return user;
}else{//说明添加失败
return retuStr;
}
}catch (FileUploadBase.FileSizeLimitExceededException e) {
e.printStackTrace();
retuStr="单个文件超出最大值!!!";
}catch (FileUploadBase.SizeLimitExceededException e) {
e.printStackTrace();
retuStr="上传文件的总的大小超出限制的最大值!!!";
}catch (Exception e) {
retuStr= "文件上传失败!";
e.printStackTrace();
}
return retuStr;
}
上传代码中用到的一些工具类:
//1、上传的各个参数所在类
public enum FieldType {
//规定表单字段
ANNOUNCECONTENT,USER_ID,USER_NAME,TIME;
//提供方法用来获取文件类型
public static FieldType getFileType(String fileType){
return valueOf(fileType.toUpperCase());
}
}
//2、时间操作类:
public class Time {
public static GregorianCalendar ggcalendar=new GregorianCalendar(Locale.CHINA);
/**
* 返回当前的年
* @return
*/
public static Integer year(){
return ggcalendar.get(Calendar.YEAR);
}
/**
* 返回当前月份
* @return
*/
public static Integer month(){
return ggcalendar.get(Calendar.MONTH)+1;
}
/**
* 返回当前是几号
* @return
*/
public static Integer date(){
return ggcalendar.get(Calendar.DATE);
}
}
//3、文件操作代码
/**
* @Method: makeFileName
* @Description: 生成上传文件的文件名,文件名以:uuid+"_"+文件的原始名称
* @param filename 文件的原始名称
* @return uuid+"_"+文件的原始名称
*/
private String makeFileName(String filename){ //2.jpg
//为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名
return UUID.randomUUID().toString() + "_" + filename;
}
/**
* 为防止一个目录下面出现太多文件,要使用hash算法打散存储
* @Method: makePath
* @Description:
* @param filename 文件名,要根据文件名生成存储目录
* @param savePath 文件存储路径
* @return 新的存储目录
*/
private String makePath(String filename,String savePath){
//得到文件名的hashCode的值,得到的就是filename这个字符串对象在内存中的地址
int hashcode = filename.hashCode();
int dir1 = hashcode&0xf; //0--15
int dir2 = (hashcode&0xf0)>>4; //0-15
//构造新的保存目录
String dir = savePath + "\\" + dir1 + "\\" + dir2; //upload\2\3 upload\3\5
return dir;
}
/**
* 该方法用来获取允许上传的文件总大小
* **/
public long getFileBufferFotTotal(){
long buffer = 200*1024*1024;//文件上传允许最大为200M
String fileSize = com.wjl.util.WebAppConfig.app("businessFileSizeForTotal");
if(fileSize!=null){//说明有设置值
fileSize = fileSize.trim();
//有设置值且是数字
if(fileSize.length()>0 && com.wjl.util.Utils.isNumeric(fileSize)){
buffer = Integer.parseInt(fileSize)*1024*1024;
}
}
return buffer;
}
/**
* 该方法用来获取允许上传的单个文件大小
* @return long:配置文件中配置的数据*1024*1024,也就是总共多少M
* **/
public long getFileBufferForSingle(){
long buffer = 10*1024*1024;//文件上传允许最大为200M
String fileSize = com.wjl.util.WebAppConfig.app("businessFileSize");
if(fileSize!=null){//说明有设置值
fileSize = fileSize.trim();
//有设置值且是数字
if(fileSize.length()>0 && com.wjl.util.Utils.isNumeric(fileSize)){
buffer = Integer.parseInt(fileSize)*1024*1024;
}
}
return buffer;
}
/**
* 该方法用来获取文件保存的路径
* */
public String getSavePath(){
String savePath = com.wjl.util.WebAppConfig.app("businessFilePath");
if(savePath!=null && savePath.trim().length()>0){
savePath = savePath.trim();
}else{
savePath = "";
}
return savePath;
}
/**
* 该方法用来获取文件临时保存的路径
* */
public String getTempPath(){
String savePath = com.wjl.util.WebAppConfig.app("businessFileTempPath");
if(savePath!=null && savePath.trim().length()>0){
savePath = savePath.trim();
}else{
savePath = "";
}
return savePath;
}
//4、读取配置文件的类:
public final class WebAppConfig {
/**
* 私有构造方法,不能创建对象
*/
private WebAppConfig() { }
/***
*
* @param key:properties文件中各个选项的name值
* @return String:与name值对应的value值
*/
public static String app(String key){
String value=null;
if(key!=null && key.trim().length()>0){
Properties prop = new Properties();
//读取指定的文件
InputStream in = WebAppConfig.class.getResourceAsStream("/webApp.properties");
try {
prop.load(in);
value = prop.getProperty(key).trim();
} catch (IOException e) {
e.printStackTrace();
}
}
return value;
}
}