基于若依的上传_代码总结
自学SpringBoot的过程中,发现开源的Ruoyi比较不错,而且作者公开全部的源码,再次谢谢作者
顺便帮这个业界良心做个推广 https://www.ruoyi.vip/ ,有兴趣学习和提高的网友们可以去学习下。
先总结,代码在下面
(1)要保存的这个表,只有一个字段是用来存储图片路径的字段;其他字段都是正常字段,和上传无关;
(2)基本思路是先上传,获得上传的文件名及路径后,再保存相关信息入数据库;
遇到的问题有
(1)不需要修改form的 enctype属性,我的代码是没有的
enctype=”multipart/form-data”
(2)用Ruoyi开源的自动生成的工具,生成的代码,但是因为这个仅仅是工具,需要改造成要符合上传图片预览的情况
所以要改原来的代码;
我参考了Ruoyi自己带的jasny.html文件 ,而Ruoyi参考的是 https://github.com/jasny/bootstrap 上传组件
(3)修改后台接收部分要修改
自动生成的是
public AjaxResult addSave(AppGoods appGoods)
保留这种方式,会报错
Failed to convert property value of type 'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile' to required type 'java.lang.String' for property 'goodsImg'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile' to required type 'java.lang.String' for property 'goodsImg': no matching editors or conversion strategy found
修改成 public AjaxResult addSave(@RequestParam(value="goodsImg",required=false) MultipartFile file,AppGoods appGoods)
还是会报错
1 | Failed to convert property value of type 'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile' to required type 'java.lang.String' for property 'goodsImg' ; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile' to required type 'java.lang.String' for property 'goodsImg' : no matching editors or conversion strategy found |
万般无奈,修改成
public AjaxResult addSave(@RequestParam(value="goodsImg",required=false) MultipartFile file, HttpServletRequest request_in)
重新按照字段进行赋值就不报错了
感觉一个一个字段赋值,有些蠢,不知道有没有更好的解决方法
代码如下
另外,ajax提交时,这个也是不能变成 contentType: "application/json"
只有 contentType: false 时,才是能够成功的,
主要是我对JS底层还是不够充分了解啊。
新增的画面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | <!DOCTYPE html> <html lang= "zh" xmlns:th= "http://www.thymeleaf.org" > <head> <th:block th:include= "include :: header('新增/goods')" /> <th:block th:include= "include :: datetimepicker-css" /> <th:block th:include= "include :: jasny-bootstrap-css" /> </head> <body class = "white-bg" > <div class = "wrapper wrapper-content animated fadeInRight ibox-content" > <form class = "form-horizontal m" id= "form-goods-add" > <div class = "form-group" > <label class = "col-sm-3 control-label" >商品名:</label> <div class = "col-sm-8" > <input id= "goodsName" name= "goodsName" class = "form-control" type= "text" > </div> </div> <div class = "form-group" > <label class = "col-sm-3 control-label" >进货价:</label> <div class = "col-sm-8" > <input id= "purchasePrice" name= "purchasePrice" class = "form-control" type= "text" > </div> </div> <div class = "form-group" > <label class = "col-sm-3 control-label" >进货日期:</label> <div class = "col-sm-8" > <div class = "input-group date" > <input id= "purchaseDate" name= "purchaseDate" class = "form-control" placeholder= "yyyy-MM-dd" type= "text" > <span class = "input-group-addon" ><i class = "fa fa-calendar" ></i></span> </div> </div> </div> <div class = "form-group" > <label class = "col-sm-3 control-label" >图片路径:</label> <div class = "col-sm-8" > <!-- <textarea name= "goodsImg" class = "form-control" ></textarea>--> <div class = "fileinput fileinput-new" data-provides= "fileinput" > <div class = "fileinput-preview thumbnail" data-trigger= "fileinput" style= "width: 400px; height: 200px;" ></div> <div > <span class = "btn btn-white btn-file" ><span class = "fileinput-new" >选择图片</span><span class = "fileinput-exists" >更改</span><input id= "goodsImg" name= "goodsImg" type= "file" ></span> <a href= "#" class = "btn btn-white fileinput-exists" data-dismiss= "fileinput" >清除</a> </div> </div> </div> </div> </form> </div> <th:block th:include= "include :: footer" /> <th:block th:include= "include :: datetimepicker-js" /> <th:block th:include= "include :: jasny-bootstrap-js" /> <script th:inline= "javascript" > var prefix = ctx + "app/goods" $( "#form-goods-add" ).validate({ focusCleanup: true }); function submitHandler() { var formData = new FormData(); if ($( '#goodsImg' )[0].files[0] == null ) { $.modal.alertWarning( "请先选择文件路径" ); return false ; } formData.append( 'goodsName' , $( "#goodsName" )[0].value); formData.append( 'purchasePrice' , $( "#purchasePrice" )[0].value); formData.append( 'purchaseDate' , $( "#purchaseDate" )[0].value); formData.append( 'goodsImg' , $( '#goodsImg' )[0].files[0]); $.ajax({ url: prefix + "/add" , type: 'post' , cache: false , data: formData, processData: false , contentType: false , dataType: "json" , success: function (result) { $.operate.successCallback(result); } }); } $( "input[name='purchaseDate']" ).datetimepicker({ format: "yyyy-mm-dd" , minView: "month" , autoclose: true }); </script> </body> </html> |
后台接收数据的Controller
package com.ruoyi.app.controller;
import java.io.IOException;
import java.text.ParseException;
import java.util.List;
import com.alibaba.fastjson.JSON;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUploadUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.app.domain.AppGoods;
import com.ruoyi.app.service.IAppGoodsService;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import javax.servlet.http.HttpServletRequest;
/**
* /goodsController
*
* @author ruoyi
* @date 2021-12-28
*/
@Controller
@RequestMapping("/app/goods")
public class AppGoodsController extends BaseController
{
private String prefix = "app/goods";
@Autowired
private IAppGoodsService appGoodsService;
@RequiresPermissions("app:goods:view")
@GetMapping()
public String goods()
{
return prefix + "/goods";
}
/**
* 查询/goods列表
*/
@RequiresPermissions("app:goods:list")
@PostMapping("/list")
@ResponseBody
public TableDataInfo list(AppGoods appGoods)
{
startPage();
List<AppGoods> list = appGoodsService.selectAppGoodsList(appGoods);
return getDataTable(list);
}
/**
* 导出/goods列表
*/
@RequiresPermissions("app:goods:export")
@Log(title = "/goods", businessType = BusinessType.EXPORT)
@PostMapping("/export")
@ResponseBody
public AjaxResult export(AppGoods appGoods)
{
List<AppGoods> list = appGoodsService.selectAppGoodsList(appGoods);
ExcelUtil<AppGoods> util = new ExcelUtil<AppGoods>(AppGoods.class);
return util.exportExcel(list, "/goods数据");
}
/**
* 新增/goods
*/
@GetMapping("/add")
public String add()
{
return prefix + "/add";
}
/**
* 新增保存/goods
*/
@RequiresPermissions("app:goods:add")
@Log(title = "/goods", businessType = BusinessType.INSERT)
@PostMapping("/add")
@ResponseBody
public AjaxResult addSave(@RequestParam(value="goodsImg",required=false) MultipartFile file)
{
AppGoods appGoods =new AppGoods();
try {
if (file != null) {
// 上传文件路径
String filePath = RuoYiConfig.getUploadPath();
// 上传并返回新文件名称
String fileName = null;
fileName = FileUploadUtils.upload(filePath, file);
appGoods.setGoodsName(getRequest().getParameter("goodsName"));
appGoods.setPurchaseDate(DateUtils.parseDate(getRequest().getParameter("purchaseDate"),new String[]{"yyyy-MM-dd"}));
appGoods.setPurchasePrice(org.apache.commons.lang3.math.NumberUtils.toScaledBigDecimal(getRequest().getParameter("purchasePrice")));
appGoods.setGoodsImg(fileName);
}
} catch (IOException | ParseException e) {
e.printStackTrace();
}
return toAjax(appGoodsService.insertAppGoods(appGoods));
}
/**
* 修改/goods
*/
@RequiresPermissions("app:goods:edit")
@GetMapping("/edit/{id}")
public String edit(@PathVariable("id") Long id, ModelMap mmap)
{
AppGoods appGoods = appGoodsService.selectAppGoodsById(id);
mmap.put("appGoods", appGoods);
return prefix + "/edit";
}
/**
* 修改保存/goods
*/
@RequiresPermissions("app:goods:edit")
@Log(title = "/goods", businessType = BusinessType.UPDATE)
@PostMapping("/edit")
@ResponseBody
//public AjaxResult editSave(AppGoods appGoods)
public AjaxResult editSave(@RequestParam(value="goodsImg",required=false) MultipartFile file)
{
AppGoods appGoods =new AppGoods();
try {
if (file != null) {
// 上传文件路径
String filePath = RuoYiConfig.getUploadPath();
// 上传并返回新文件名称
String fileName = null;
fileName = FileUploadUtils.upload(filePath, file);
appGoods.setId(new Long(getRequest().getParameter("id")));
appGoods.setGoodsName(getRequest().getParameter("goodsName"));
appGoods.setPurchaseDate(DateUtils.parseDate(getRequest().getParameter("purchaseDate"),new String[]{"yyyy-MM-dd"}));
appGoods.setPurchasePrice(org.apache.commons.lang3.math.NumberUtils.toScaledBigDecimal(getRequest().getParameter("purchasePrice")));
appGoods.setGoodsImg(fileName);
}
} catch (IOException | ParseException e) {
e.printStackTrace();
}
return toAjax(appGoodsService.updateAppGoods(appGoods));
}
/**
* 删除/goods
*/
@RequiresPermissions("app:goods:remove")
@Log(title = "/goods", businessType = BusinessType.DELETE)
@PostMapping( "/remove")
@ResponseBody
public AjaxResult remove(String ids)
{
return toAjax(appGoodsService.deleteAppGoodsByIds(ids));
}
}
修改页面代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | <!DOCTYPE html> <html lang= "zh" xmlns:th= "http://www.thymeleaf.org" > <head> <th:block th:include= "include :: header('修改商品')" /> <th:block th:include= "include :: datetimepicker-css" /> <th:block th:include= "include :: jasny-bootstrap-css" /> </head> <body class = "white-bg" > <div class = "wrapper wrapper-content animated fadeInRight ibox-content" > <form class = "form-horizontal m" id= "form-goods-edit" th:object= "${appGoods}" > <input name= "id" th:field= "*{id}" type= "hidden" > <div class = "form-group" > <label class = "col-sm-3 control-label" >商品名:</label> <div class = "col-sm-8" > <input name= "goodsName" id= "goodsName" th:field= "*{goodsName}" class = "form-control" type= "text" > </div> </div> <div class = "form-group" > <label class = "col-sm-3 control-label" >进货价:</label> <div class = "col-sm-8" > <input name= "purchasePrice" id= "purchasePrice" th:field= "*{purchasePrice}" class = "form-control" type= "text" > </div> </div> <div class = "form-group" > <label class = "col-sm-3 control-label" >进货日期:</label> <div class = "col-sm-8" > <div class = "input-group date" > <input id= "purchaseDate" name= "purchaseDate" th:value= "${#dates.format(appGoods.purchaseDate, 'yyyy-MM-dd')}" class = "form-control" placeholder= "yyyy-MM-dd" type= "text" > <span class = "input-group-addon" ><i class = "fa fa-calendar" ></i></span> </div> </div> </div> <div class = "form-group" > <label class = "col-sm-3 control-label" >图片</label> <div class = "col-sm-8" > <div class = "fileinput fileinput-new" data-provides= "fileinput" > <div class = "fileinput-new thumbnail" style= "width: 400px; height: 200px;" > <!-- <img th:src= "@{/img/profile.jpg}" >--> <img th:src= "*{goodsImg}" /> </div> <div class = "fileinput-preview fileinput-exists thumbnail" style= "max-width: 200px; max-height: 150px;" ></div> <div> <span class = "btn btn-white btn-file" ><span class = "fileinput-new" >选择图片</span><span class = "fileinput-exists" >更改</span><input id= "goodsImg" name= "goodsImg" type= "file" ></span> <a href= "#" class = "btn btn-white fileinput-exists" data-dismiss= "fileinput" >清除</a> </div> </div> </div> </div> </form> </div> <th:block th:include= "include :: footer" /> <th:block th:include= "include :: datetimepicker-js" /> <th:block th:include= "include :: jasny-bootstrap-js" /> <script th:inline= "javascript" > var prefix = ctx + "app/goods" ; $( "#form-goods-edit" ).validate({ focusCleanup: true }); // function submitHandler() { // if ($.validate.form()) { // $.operate.save(prefix + "/edit", $('#form-goods-edit').serialize()); // } // } function submitHandler() { //debugger; var formData = new FormData(); if ($( '#goodsImg' )[0].files[0] == null ) { $.modal.alertWarning( "请先选择文件路径" ); return false ; } formData.append( 'id' , $( "#id" )[0].value); formData.append( 'goodsName' , $( "#goodsName" )[0].value); //alert($("#goodsName")[0].value); formData.append( 'purchasePrice' , $( "#purchasePrice" )[0].value); formData.append( 'purchaseDate' , $( "#purchaseDate" )[0].value); formData.append( 'goodsImg' , $( '#goodsImg' )[0].files[0]); $.ajax({ url: prefix + "/edit" , type: 'post' , cache: false , data: formData, processData: false , contentType: false , //contentType: "application/json", //--错误的配置 dataType: "json" , success: function (result) { $.operate.successCallback(result); } }); } $( "input[name='purchaseDate']" ).datetimepicker({ format: "yyyy-mm-dd" , minView: "month" , autoclose: true }); </script> </body> </html> |
列表页面代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | <!DOCTYPE html> <html lang= "zh" xmlns:th= "http://www.thymeleaf.org" xmlns:shiro= "http://www.pollix.at/thymeleaf/shiro" > <head> <th:block th:include= "include :: header('商品列表')" /> </head> <body class = "gray-bg" > <div class = "container-div" > <div class = "row" > <div class = "col-sm-12 search-collapse" > <form id= "formId" > <div class = "select-list" > <ul> <li> <label>商品名:</label> <input type= "text" name= "goodsName" /> </li> <li> <label>进货价:</label> <input type= "text" name= "purchasePrice" /> </li> <li> <label>进货日期:</label> <input type= "text" class = "time-input" placeholder= "请选择进货日期" name= "purchaseDate" /> </li> <li> <a class = "btn btn-primary btn-rounded btn-sm" onclick= "$.table.search()" ><i class = "fa fa-search" ></i> 搜索</a> <a class = "btn btn-warning btn-rounded btn-sm" onclick= "$.form.reset()" ><i class = "fa fa-refresh" ></i> 重置</a> </li> </ul> </div> </form> </div> <div class = "btn-group-sm" id= "toolbar" role= "group" > <a class = "btn btn-success" onclick= "$.operate.add()" shiro:hasPermission= "app:goods:add" > <i class = "fa fa-plus" ></i> 添加 </a> <a class = "btn btn-primary single disabled" onclick= "$.operate.edit()" shiro:hasPermission= "app:goods:edit" > <i class = "fa fa-edit" ></i> 修改 </a> <a class = "btn btn-danger multiple disabled" onclick= "$.operate.removeAll()" shiro:hasPermission= "app:goods:remove" > <i class = "fa fa-remove" ></i> 删除 </a> <a class = "btn btn-warning" onclick= "$.table.exportExcel()" shiro:hasPermission= "app:goods:export" > <i class = "fa fa-download" ></i> 导出 </a> </div> <div class = "col-sm-12 select-table table-striped" > <table id= "bootstrap-table" ></table> </div> </div> </div> <th:block th:include= "include :: footer" /> <script th:inline= "javascript" > var editFlag = [[${@permission.hasPermi( 'app:goods:edit' )}]]; var removeFlag = [[${@permission.hasPermi( 'app:goods:remove' )}]]; var prefix = ctx + "app/goods" ; $( function () { var options = { url: prefix + "/list" , createUrl: prefix + "/add" , updateUrl: prefix + "/edit/{id}" , removeUrl: prefix + "/remove" , exportUrl: prefix + "/export" , modalName: "商品" , columns: [{ checkbox: true }, { field: 'id' , title: '' , visible: false }, { field: 'goodsName' , title: '商品名' }, { field: 'purchasePrice' , title: '进货价' }, { field: 'purchaseDate' , title: '进货日期' }, { field: 'goodsImg' , title: '图片' , formatter: function (value, row, index) { // 图片预览(注意:如存储在本地直接获取数据库路径,如有配置context-path需要使用ctx+路径) // 如:/profile/upload/2019/08/08/3b7a839aced67397bac694d77611ce72.png return $.table.imageView(row.goodsImg); } }, { title: '操作' , align: 'center' , formatter: function (value, row, index) { var actions = []; actions.push( '<a class="btn btn-success btn-xs ' + editFlag + '" href="javascript:void(0)" onclick="$.operate.edit(\'' + row.id + '\')"><i class="fa fa-edit"></i>编辑</a> ' ); actions.push( '<a class="btn btn-danger btn-xs ' + removeFlag + '" href="javascript:void(0)" onclick="$.operate.remove(\'' + row.id + '\')"><i class="fa fa-remove"></i>删除</a>' ); return actions.join( '' ); } }] }; $.table.init(options); }); </script> </body> </html> |
很久没有写JAVA代码了,记录一下~
2022年1月5日重新修改了Controller代码
修改了列表及修改代码
再次感谢若依的作者,真的不错。TEXT类型的字段,能够单独生成富文本模式,且支持上传
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)