Spring MVC 实现文件上传
时序图
利用 Spring MVC 实现文件上传功能,离不开对 MultipartResolver 的设置。MultipartResolver 这个类,你可以将其视为 Spring MVC 实现文件上传功能时的工具类,这个类也只会在文件上传中发挥作用。在配置了具体实现类之后,Spring MVC 中的 DispatcherServlet 在处理请求时会调用 MultipartResolver 中的方法判断此请求是不是文件上传请求。如果是,DispatcherServlet 将调用 MultipartResolver 的 resolveMultipart(request) 方法对该请求对象进行装饰并返回一个新的 MultipartHttpServletRequest 供后继处理流程使用。注意,此时的请求对象会由 HttpServletRequest 类型转换成 MultipartHttpServletRequest 类型,这个类中会包含所上传的文件对象,可供后续流程直接使用,而无需自行在代码中实现对文件内容的读取逻辑。
当收到请求时,DispatcherServlet 的 checkMultipart() 方法会调用 MultipartResolver 的 isMultipart() 方法判断请求中是否包含文件。
如果请求数据中包含文件,则调用 MultipartResolver 的 resolveMultipart() 方法对请求的数据进行解析,然后将文件数据解析成 MultipartFile 并封装在 MultipartHttpServletRequest(继承了 HttpServletRequest)对象中,最后传递给 Controller 控制器。
图片上传实现
实现文件上传时需要依赖相关 Jar 包,我们首先在 pom 文件中将依赖包添加进来:
-
pom.xml 。
<!-- Start: commons相关依赖包 --> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>${commons-io.version}</version> </dependency> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>${commons-fileupload.version}</version> </dependency> <!-- Start: commons相关依赖包 -->
-
spring-mvc.xml
-
如下设置 MultipartResolver,我们使用的是仍是 CommonsMultipartResolver 实现类:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- 设定默认编码 --> <property name="defaultEncoding" value="UTF-8"></property> <!-- 设定文件上传的最大值为5MB,5*1024*1024 --> <property name="maxUploadSize" value="5242880"></property> </bean>
-
LoadImageController.java 。
通过前文中的分析,可知文件对象已被封装到 MultipartFile 对象中,在代码中可以直接使用此文件对象,之后调用 File 相关方法将文件存储到 upload 目录下,代码如下:
public Result upload(HttpServletRequest request, @RequestParam("file") MultipartFile file) throws IOException { ServletContext sc = request.getSession().getServletContext(); String dir = sc.getRealPath("/upload"); String type = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".") + 1, file.getOriginalFilename().length()); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss"); Random r = new Random(); String imgName = ""; if ("jpg".equals(type)) { imgName = sdf.format(new Date()) + r.nextInt(100) + ".jpg"; } else if ("png".equals(type)) { imgName = sdf.format(new Date()) + r.nextInt(100) + ".png"; } else if ("jpeg".equals(type)) { imgName = sdf.format(new Date()) + r.nextInt(100) + ".jpeg"; } else if ("gif".equals(type)) { imgName = sdf.format(new Date()) + r.nextInt(100) + ".gif"; } else { return null; } //将文件流写入到磁盘中 FileUtils.writeByteArrayToFile(new File(dir, imgName), file.getBytes()); //返回文件路径 return Result.ok().put("url", "/upload/" + imgName); }
这样图片上传至本地的过程就完成了
实现图片管理模块
流程设计
图片管理模块主要包括列表,添加,编辑,删除功能。
列表功能的实现流程。这里我们选用JqGrid作为分页的功能插件。
添加功能的具体过程为:点击“添加”按钮 - >选择图片并上传 - >填写备注信息 - >保存至数据库。
修改编辑功能的流程图与添加功能类似,唯一的区别是点击修改按钮前需要把对应的预览图和备注从数据库获取并展示在修改界面,让用户知道修改的是哪个图片。
删除功能基本流程为,在数据列表区选择想要删除的数据,之后点击“删除”按钮,触发删除()事件并向后端发送删除请求,成功后重新刷新列表数据。
页面与交互
图片模块页面构成主要为主页面和信息编辑弹框。
功能划分
主页面的设计效果图,如下所示:
如上图所示,图片模块页面的布局组成为:模块标题区域,模块功能区域。
其中,模块功能区又包含功能按钮区域,列表信息区域和分页信息区域。
信息编辑弹框设计效果图,如下所示:
由上图可知,信息编辑弹框区域的组成为:
- 标题区域; - 错误提示区域; - 图片预览区域; - 上传按钮; - 信息输入区; - 表单提交区域。
操作
主页面包括如下操作:
- 按钮点击; - 记录选择; - 翻页。
添加/修改按钮点击后会出现信息编辑弹框,此时又会产生如下操作:
- 文件上传; - 信息输入; - 请求提交。
反馈效果
接下来,我们看下图片模块包含哪些交互,交互过程是怎样的。
- 列表数据重新加载:页面初始化时或者点击分页按钮时,JqGrid会对列表数据进行渲染及重新渲染。
- 弹框:点击“添加”或者“修改”按钮后会显示信息编辑弹框。
- 选中提示:点击“编辑”按钮前,如果未选中一条编辑记录或者选中了多条编辑记录,都会弹出此提示。点击删除按钮前,如果未选中记录也会出现此提示。
- 错误提示区显示:用户信息输入不规范会看到此错误提示。
- 请求处理完成提示:添加请求,修改请求,删除请求完成后会出现此提示。
- 页面跳转。页面跳转方向主要有:
跳入:点击导航栏的“图片管理”会进入此页面。无操作:未点击功能按钮或者输入信息错误则不跳转。跳出:身份认证失败会进入登录页面。
前端实现
前端页面代码文件,我们命名为picture.html
初始化上传者
前端的文件上传插件,我们使用的是JQuery的ajaxupload工具。接下来,带大家了解如何在前端页面中使用它。
首先,在页面中引入依赖文件:
<!-- ajax upload --> <script src="plugins/ajaxupload/ajaxupload.js"></script>
然后,设置上传按钮DOM对象:
<div class="col-sm-10"> <a class="btn btn-info" id="upload"><i class="fa fa-picture-o"></i> 上传文件</a> </div>
上传代码逻辑如下,首先判断文件格式,图片上传限制文件格式为jpg,png,gif,其他格式的文件将不会被处理,之后向后端发送文件上传请求,并根据后端返回数据进行相应的事件处理。
1 new AjaxUpload('#upload', { 2 action: 'images', 3 name: 'file', 4 autoSubmit: true, 5 responseType: "json", 6 onSubmit: function (file, extension) { 7 if (!(extension && /^(jpg|jpeg|png|gif)$/.test(extension.toLowerCase()))) { 8 alert('只支持jpg、png、gif格式的图片!', { 9 icon: "error", 10 }); 11 return false; 12 } 13 }, 14 onComplete: function (file, r) { 15 if (r.resultCode == 200) { 16 alert("上传成功"); 17 $("#picturePath").val(r.data); 18 $("#img").attr("src", r.data); 19 $("#img").attr("style", " 100px;height: 100px;display:block;"); 20 return false; 21 } else { 22 alert(r.message); 23 } 24 } 25 });
以下通过注释对ajaxupload插件初始化时的主要参数均做了说明:
new AjaxUpload('#upload', {//上传按钮DOM //文件上传后台处理url action: 'images', //参数名称,对应的是Controller中的 @RequestParam("file") MultipartFile file,如果两个名称不同则会报错 name: 'file', //是否自动提交 autoSubmit: true, //服务器返回的数据类型 responseType: "json", //请求提交前执行的函数 onSubmit: function (file, extension) { }, //请求完成后的回调函数 onComplete: function (file, r) { } });
功能代码
在信息编辑弹框页面中,当文件上传完成,备注信息输入完成后点击“确认”按钮,首先会执行validObject()方法校验输入参数,校验逻辑通过后则进行数据封装,并发送网络请求至后端。之后根据后端返回的结果对象进行对应的操作,如果出现报错则直接提醒用户错误信息,如果后端返回成功则根据不同的resultCode进行对应的操作.resultCode等于200,则表示请求成功,关闭弹框,提示用户保存成功并重新加载图片信息列表数据.
1 $('#saveButton').click(function () { 2 //验证数据 3 if (validObject()) { 4 //一切正常后发送网络请求 5 //ajax 6 var id = $("#pictureId").val(); 7 var picturePath = $("#picturePath").val(); 8 var pictureRemark = $("#pictureRemark").val(); 9 var data = {"path": picturePath, "remark": pictureRemark}; 10 $.ajax({ 11 type: 'POST',//方法类型 12 dataType: "json",//预期服务器返回的数据类型 13 url: pictures/save,//url 14 contentType: "application/json; charset=utf-8", 15 beforeSend: function (request) { 16 //设置header值 17 request.setRequestHeader("token", getCookie("token")); 18 }, 19 data: JSON.stringify(data), 20 success: function (result) { 21 checkResultCode(result.resultCode); 22 if (result.resultCode == 200) { 23 $('#pictureModal').modal('hide'); 24 alert("保存成功"); 25 reload(); 26 } else { 27 $('#pictureModal').modal('hide'); 28 alert("保存失败"); 29 }; 30 } 31 }); 32 33 } 34 });
后端实现
表结构设计
新增tb ssm图片用来存储图片信息,建表语句如下:
use gitchat_ssm_demo_db; DROP TABLE IF EXISTS `tb_ssm_picture`; CREATE TABLE `tb_ssm_picture` ( `id` bigint(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id', `path` varchar(200) NOT NULL DEFAULT '' COMMENT '图片路径', `remark` varchar(200) NOT NULL DEFAULT '' COMMENT '备注', `is_deleted` tinyint(4) NOT NULL DEFAULT 0 COMMENT '是否已删除 0未删除 1已删除', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DAO层
1 <select id="findPictures" parameterType="Map" resultMap="PictureResult"> 2 select id,path,remark,create_time from tb_ssm_picture 3 where is_deleted=0 4 order by id desc 5 <if test="start!=null and limit!=null"> 6 limit #{start},#{limit} 7 </if> 8 </select> 9 10 <select id="getTotalPictures" parameterType="Map" resultType="int"> 11 select count(*) from tb_ssm_picture 12 where is_deleted=0 13 </select> 14 15 <insert id="insertPicture" parameterType="com.ssm.demo.entity.Picture"> 16 insert into tb_ssm_picture(path,remark) 17 values(#{path},#{remark}) 18 </insert> 19 20 <update id="updPicture" parameterType="com.ssm.demo.entity.Picture"> 21 update tb_ssm_picture 22 set 23 path=#{path},remark=#{remark} 24 where id=#{id} and is_deleted=0 25 </update> 26 27 <update id="delPicture" parameterType="int"> 28 update tb_ssm_picture 29 set is_deleted=1 where id=#{id} 30 </update> 31 32 <select id="findPictureById" parameterType="int" resultMap="PictureResult"> 33 select id,path,remark,create_time 34 from tb_ssm_picture where id=#{id} and is_deleted=0 35 </select> 36 37 //删除功能是使用的逻辑删除 38 <update id="deleteBatch"> 39 update tb_ssm_picture 40 set is_deleted=1 where id in 41 <foreach item="id" collection="array" open="(" separator="," close=")"> 42 #{id} 43 </foreach> 44 </update>
Service 层
新增业务层代码方法 getPicturePage() 、 save() 、 update() 、 deleteBatch ,分别对应图片信息的分页查询、新增功能、修改功能和删除功能。业务代码的具体实现逻辑为调用 DAO 层中的方法对 MySQL 进行数据查询及数据更改。
@Override public PageResult getPicturePage(PageUtil pageUtil) { List<Picture> pictures = pictureDao.findPictures(pageUtil); int total = pictureDao.getTotalPictures(pageUtil); PageResult pageResult = new PageResult(pictures, total, pageUtil.getLimit(), pageUtil.getPage()); return pageResult; } @Override public Picture queryObject(Integer id) { return pictureDao.findPictureById(id); } @Override public int save(Picture picture) { return pictureDao.insertPicture(picture); } @Override public int update(Picture picture) { return pictureDao.updPicture(picture); } @Override public int delete(Integer id) { return pictureDao.delPicture(id); } @Override public int deleteBatch(Integer[] ids) { return pictureDao.deleteBatch(ids); }
Controller 层
控制层代码逻辑主要为参数校验、请求校验,对前端提交的请求进行路由和方法实现,之后根据方法返回封装 Result 对象并返回至前端,以下为图片信息管理功能模块所有方法的实现代码:
1 /** 2 * 列表 3 */ 4 @RequestMapping("/list") 5 public Result list(@RequestParam Map<String, Object> params) { 6 if (StringUtils.isEmpty(params.get("page")) || StringUtils.isEmpty(params.get("limit"))) { 7 return ResultGenerator.genErrorResult(Constants.RESULT_CODE_PARAM_ERROR, "参数异常!"); 8 } 9 //查询列表数据 10 PageUtil pageUtil = new PageUtil(params); 11 return ResultGenerator.genSuccessResult(pictureService.getPicturePage(pageUtil)); 12 } 13 14 /** 15 * 信息 16 */ 17 @RequestMapping("/info/{id}") 18 public Result info(@PathVariable("id") Integer id, @TokenToUser AdminUser loginUser) { 19 if (loginUser == null) { 20 return ResultGenerator.genErrorResult(Constants.RESULT_CODE_NOT_LOGIN, "未登录!"); 21 } 22 if (id < 1) { 23 return ResultGenerator.genErrorResult(Constants.RESULT_CODE_PARAM_ERROR, "参数异常!"); 24 } 25 Picture picture = pictureService.queryObject(id); 26 if (picture == null) { 27 return ResultGenerator.genErrorResult(Constants.RESULT_CODE_PARAM_ERROR, "参数异常!"); 28 } 29 return ResultGenerator.genSuccessResult(picture); 30 } 31 32 /** 33 * 保存 34 */ 35 @RequestMapping("/save") 36 public Result save(@RequestBody Picture picture, @TokenToUser AdminUser loginUser) { 37 if (loginUser == null) { 38 return ResultGenerator.genErrorResult(Constants.RESULT_CODE_NOT_LOGIN, "未登录!"); 39 } 40 if (StringUtils.isEmpty(picture.getPath()) || StringUtils.isEmpty(picture.getRemark())) { 41 return ResultGenerator.genErrorResult(Constants.RESULT_CODE_PARAM_ERROR, "参数异常!"); 42 } 43 if (pictureService.save(picture) > 0) { 44 return ResultGenerator.genSuccessResult(); 45 } else { 46 return ResultGenerator.genFailResult("添加失败"); 47 } 48 } 49 50 /** 51 * 修改 52 */ 53 @RequestMapping("/update") 54 public Result update(@RequestBody Picture picture, @TokenToUser AdminUser loginUser) { 55 if (loginUser == null) { 56 return ResultGenerator.genErrorResult(Constants.RESULT_CODE_NOT_LOGIN, "未登录!"); 57 } 58 if (null == picture.getId() || StringUtils.isEmpty(picture.getPath()) || StringUtils.isEmpty(picture.getRemark())) { 59 return ResultGenerator.genErrorResult(Constants.RESULT_CODE_PARAM_ERROR, "参数异常!"); 60 } 61 Picture tempPicture = pictureService.queryObject(picture.getId()); 62 if (tempPicture == null) { 63 return ResultGenerator.genErrorResult(Constants.RESULT_CODE_PARAM_ERROR, "参数异常!"); 64 } 65 if (pictureService.update(picture) > 0) { 66 return ResultGenerator.genSuccessResult(); 67 } else { 68 return ResultGenerator.genFailResult("修改失败"); 69 } 70 } 71 72 /** 73 * 删除 74 */ 75 @RequestMapping("/delete") 76 public Result delete(@RequestBody Integer[] ids, @TokenToUser AdminUser loginUser) { 77 if (loginUser == null) { 78 return ResultGenerator.genErrorResult(Constants.RESULT_CODE_NOT_LOGIN, "未登录!"); 79 } 80 if (ids.length < 1) { 81 return ResultGenerator.genErrorResult(Constants.RESULT_CODE_PARAM_ERROR, "参数异常!"); 82 } 83 if (pictureService.deleteBatch(ids) > 0) { 84 return ResultGenerator.genSuccessResult(); 85 } else { 86 return ResultGenerator.genFailResult("删除失败"); 87 } 88 }
删除功能
删除功能通常分为逻辑删除和物理删除,逻辑删除是名义上的删除,而物理删除是真正的删除。
举个简单的例子,仓库的货架上堆放着各种商品,管理员在取商品的时候不是直接去货架找,而是通过货架商品单上记录的信息去找,按照单子上面记载的位置再去找商品。逻辑删除相当于把货架商品单上的记录用线划掉,表明这件商品已经卖出去了或者已经处理掉了,可实际上物品并没有被拿走还是放在库里,只不过是被标记为 “ 已处理 ” 即不能再进行出库操作了。而物理删除则是把仓库货架上的商品实实在在扔掉或者处理掉了,即货架中根本没有这件商品了。
两者在编码实现时的区别是使用 delete 语句还是 update 语句。
比如,物理删除的实现代码为:
delete from tb_xxx where id = 10
而逻辑删除的实现代码为:
update tb_xxx set is_deleted=1 where id = 10
在实际开发过程中,删除数据一定要慎重,对于重要的数据,最好不要轻易物理删除(即直接删除),在必要的情况下可以使用逻辑删除的方法,即设置一个删除标志的列属性表示逻辑删除,比如本项目中使用的就是 is_deleted 字段来标识记录是否被删除。
具体的js代码
1 $(function () { 2 //隐藏弹框 3 $('#pictureModal').modal('hide'); 4 //隐藏错误提示框 5 $('.alert-danger').css("display", "none"); 6 7 //modal绑定hide事件 8 $('#pictureModal').on('hide.bs.modal', function () { 9 reset(); 10 }); 11 $("#jqGrid").jqGrid({ 12 url: 'pictures/list', 13 datatype: "json", 14 colModel: [ 15 {label: 'id', name: 'id', index: 'id', 50, sortable: false, hidden: true, key: true}, 16 {label: '图片预览', name: 'path', index: 'path', sortable: false, 105, formatter: imgFormatter}, 17 {label: '图片备注', name: 'remark', index: 'remark', sortable: false, 105}, 18 {label: '添加时间', name: 'createTime', index: 'createTime', sortable: true, 80} 19 ], 20 height: 385, 21 rowNum: 10, 22 rowList: [10, 30, 50], 23 styleUI: 'Bootstrap', 24 loadtext: '信息读取中...', 25 rownumbers: true, 26 rownumWidth: 25, 27 auto true, 28 multiselect: true, 29 pager: "#jqGridPager", 30 jsonReader: { 31 root: "data.list", 32 page: "data.currPage", 33 total: "data.totalPage", 34 records: "data.totalCount" 35 }, 36 prmNames: { 37 page: "page", 38 rows: "limit", 39 order: "order" 40 }, 41 gridComplete: function () { 42 //隐藏grid底部滚动条 43 $("#jqGrid").closest(".ui-jqgrid-bdiv").css({"overflow-x": "hidden"}); 44 } 45 }); 46 47 //预览图的设置 48 function imgFormatter(cellvalue) { 49 return "<a href='" + cellvalue + "'> <img src='" + cellvalue + "' height="120" width="135" alt='SSM'/></a>"; 50 } 51 52 // 上传前的预览图展示和上传照片步骤 53 new AjaxUpload('#upload', { 54 action: 'images', 55 name: 'file', 56 autoSubmit: true, 57 responseType: "json", 58 onSubmit: function (file, extension) { 59 if (!(extension && /^(jpg|jpeg|png|gif)$/.test(extension.toLowerCase()))) { 60 alert('只支持jpg、png、gif格式的图片!', { 61 icon: "error", 62 }); 63 return false; 64 } 65 }, 66 onComplete: function (file, r) { 67 if (r.resultCode == 200) { 68 alert("上传成功"); 69 $("#picturePath").val(r.data); 70 $("#img").attr("src", r.data); 71 console.log("r.data="+r.data); 72 $("#img").attr("style", " 100px;height: 100px;display:block;"); 73 return false; 74 } else { 75 alert(r.message); 76 } 77 } 78 }); 79 }); 80 81 //绑定modal上的保存按钮 82 $('#saveButton').click(function () { 83 //验证数据 84 if (validObject()) { 85 //一切正常后发送网络请求 86 //ajax 87 var id = $("#pictureId").val(); 88 var picturePath = $("#picturePath").val(); 89 var pictureRemark = $("#pictureRemark").val(); 90 var data = {"path": picturePath, "remark": pictureRemark}; 91 var url = 'pictures/save'; 92 //id>0表示编辑操作 93 if (id > 0) { 94 data = {"id": id, "path": picturePath, "remark": pictureRemark}; 95 url = 'pictures/update'; 96 } 97 $.ajax({ 98 type: 'POST',//方法类型 99 dataType: "json",//预期服务器返回的数据类型 100 url: url,//url 101 contentType: "application/json; charset=utf-8", 102 beforeSend: function (request) { 103 //设置header值 104 request.setRequestHeader("token", getCookie("token")); 105 }, 106 data: JSON.stringify(data), 107 success: function (result) { 108 checkResultCode(result.resultCode); 109 if (result.resultCode == 200) { 110 $('#pictureModal').modal('hide'); 111 alert("保存成功"); 112 reload(); 113 } 114 else { 115 $('#pictureModal').modal('hide'); 116 alert("保存失败"); 117 } 118 ; 119 }, 120 error: function () { 121 alert("操作失败"); 122 } 123 }); 124 } 125 }); 126 127 function pictureAdd() { 128 reset(); 129 $('.modal-title').html('图片添加'); 130 $('#pictureModal').modal('show'); 131 } 132 133 function pictureEdit() { 134 reset(); 135 $('.modal-title').html('修改图片'); 136 137 var id = getSelectedRow(); 138 if (id == null) { 139 return; 140 } 141 //请求数据 142 $.ajax({ 143 type: "GET", 144 url: "pictures/info/" + id, 145 contentType: "application/json", 146 beforeSend: function (request) { 147 //设置header值 148 request.setRequestHeader("token", getCookie("token")); 149 }, 150 success: function (r) { 151 checkResultCode(r.resultCode); 152 if (r.resultCode == 200 && r.data != null) { 153 //填充数据至modal 154 console.log(r.data); 155 $('#pictureId').val(r.data.id); 156 $("#img").attr("src", r.data.path); 157 $("#img").attr("style", " 100px;height: 100px;display:block;"); 158 $('#picturePath').val(r.data.path); 159 $('#pictureRemark').val(r.data.remark); 160 } 161 } 162 }); 163 //显示modal 164 $('#pictureModal').modal('show'); 165 } 166 167 /** 168 * 数据验证 169 */ 170 function validObject() { 171 var picturePath = $('#picturePath').val(); 172 if (isNull(picturePath)) { 173 showErrorInfo("图片不能为空!"); 174 return false; 175 } 176 var pictureRemark = $('#pictureRemark').val(); 177 if (isNull(pictureRemark)) { 178 showErrorInfo("备注信息不能为空!"); 179 return false; 180 } 181 if (!validLength(pictureRemark, 150)) { 182 showErrorInfo("备注信息长度不能大于150!"); 183 return false; 184 } 185 if (!validLength(picturePath, 120)) { 186 showErrorInfo("图片上传有误!"); 187 return false; 188 } 189 return true; 190 } 191 192 /** 193 * 重置 194 */ 195 function reset() { 196 //隐藏错误提示框 197 $('.alert-danger').css("display", "none"); 198 //清空数据 199 $('#pictureId').val(0); 200 $('#picturePath').val(''); 201 $('#pictureRemark').val(''); 202 $("#img").attr("style", "display:none;"); 203 } 204 205 function deletePicture() { 206 var ids = getSelectedRows(); 207 if (ids == null) { 208 return; 209 } 210 $.ajax({ 211 type: "POST", 212 url: "pictures/delete", 213 contentType: "application/json", 214 beforeSend: function (request) { 215 //设置header值 216 request.setRequestHeader("token", getCookie("token")); 217 }, 218 data: JSON.stringify(ids), 219 success: function (r) { 220 checkResultCode(r.resultCode); 221 if (r.resultCode == 200) { 222 alert("删除成功"); 223 $("#jqGrid").trigger("reloadGrid"); 224 } else { 225 alert(r.message); 226 } 227 } 228 }); 229 } 230 231 /** 232 * jqGrid重新加载 233 */ 234 function reload() { 235 reset(); 236 var page = $("#jqGrid").jqGrid('getGridParam', 'page'); 237 $("#jqGrid").jqGrid('setGridParam', { 238 page: page 239 }).trigger("reloadGrid"); 240 }