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,其他格式的文件将不会被处理,之后向后端发送文件上传请求,并根据后端返回数据进行相应的事件处理。

  new AjaxUpload('#upload', {
action: 'images',
name: 'file',
autoSubmit: true,
responseType: "json",
onSubmit: function (file, extension) {
if (!(extension && /^(jpg|jpeg|png|gif)$/.test(extension.toLowerCase()))) {
alert('只支持jpg、png、gif格式的图片!', {
icon: "error",
});
return false;
}
},
onComplete: function (file, r) {
if (r.resultCode == 200) {
alert("上传成功");
$("#picturePath").val(r.data);
$("#img").attr("src", r.data);
$("#img").attr("style", "width: 100px;height: 100px;display:block;");
return false;
} else {
alert(r.message);
}
}
});

以下通过注释对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,则表示请求成功,关闭弹框,提示用户保存成功并重新加载图片信息列表数据.

 $('#saveButton').click(function () {
//验证数据
if (validObject()) {
//一切正常后发送网络请求
//ajax
var id = $("#pictureId").val();
var picturePath = $("#picturePath").val();
var pictureRemark = $("#pictureRemark").val();
var data = {"path": picturePath, "remark": pictureRemark};
$.ajax({
type: 'POST',//方法类型
dataType: "json",//预期服务器返回的数据类型
url: pictures/save,//url
contentType: "application/json; charset=utf-8",
beforeSend: function (request) {
//设置header值
request.setRequestHeader("token", getCookie("token"));
},
data: JSON.stringify(data),
success: function (result) {
checkResultCode(result.resultCode);
if (result.resultCode == 200) {
$('#pictureModal').modal('hide');
alert("保存成功");
reload();
} else {
$('#pictureModal').modal('hide');
alert("保存失败");
};
}
}); }
});

后端实现

表结构设计

新增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层

 <select id="findPictures" parameterType="Map" resultMap="PictureResult">
select id,path,remark,create_time from tb_ssm_picture
where is_deleted=0
order by id desc
<if test="start!=null and limit!=null">
limit #{start},#{limit}
</if>
</select> <select id="getTotalPictures" parameterType="Map" resultType="int">
select count(*) from tb_ssm_picture
where is_deleted=0
</select> <insert id="insertPicture" parameterType="com.ssm.demo.entity.Picture">
insert into tb_ssm_picture(path,remark)
values(#{path},#{remark})
</insert> <update id="updPicture" parameterType="com.ssm.demo.entity.Picture">
update tb_ssm_picture
set
path=#{path},remark=#{remark}
where id=#{id} and is_deleted=0
</update> <update id="delPicture" parameterType="int">
update tb_ssm_picture
set is_deleted=1 where id=#{id}
</update> <select id="findPictureById" parameterType="int" resultMap="PictureResult">
select id,path,remark,create_time
from tb_ssm_picture where id=#{id} and is_deleted=0
</select> //删除功能是使用的逻辑删除
<update id="deleteBatch">
update tb_ssm_picture
set is_deleted=1 where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</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 对象并返回至前端,以下为图片信息管理功能模块所有方法的实现代码:

 /**
* 列表
*/
@RequestMapping("/list")
public Result list(@RequestParam Map<String, Object> params) {
if (StringUtils.isEmpty(params.get("page")) || StringUtils.isEmpty(params.get("limit"))) {
return ResultGenerator.genErrorResult(Constants.RESULT_CODE_PARAM_ERROR, "参数异常!");
}
//查询列表数据
PageUtil pageUtil = new PageUtil(params);
return ResultGenerator.genSuccessResult(pictureService.getPicturePage(pageUtil));
} /**
* 信息
*/
@RequestMapping("/info/{id}")
public Result info(@PathVariable("id") Integer id, @TokenToUser AdminUser loginUser) {
if (loginUser == null) {
return ResultGenerator.genErrorResult(Constants.RESULT_CODE_NOT_LOGIN, "未登录!");
}
if (id < 1) {
return ResultGenerator.genErrorResult(Constants.RESULT_CODE_PARAM_ERROR, "参数异常!");
}
Picture picture = pictureService.queryObject(id);
if (picture == null) {
return ResultGenerator.genErrorResult(Constants.RESULT_CODE_PARAM_ERROR, "参数异常!");
}
return ResultGenerator.genSuccessResult(picture);
} /**
* 保存
*/
@RequestMapping("/save")
public Result save(@RequestBody Picture picture, @TokenToUser AdminUser loginUser) {
if (loginUser == null) {
return ResultGenerator.genErrorResult(Constants.RESULT_CODE_NOT_LOGIN, "未登录!");
}
if (StringUtils.isEmpty(picture.getPath()) || StringUtils.isEmpty(picture.getRemark())) {
return ResultGenerator.genErrorResult(Constants.RESULT_CODE_PARAM_ERROR, "参数异常!");
}
if (pictureService.save(picture) > 0) {
return ResultGenerator.genSuccessResult();
} else {
return ResultGenerator.genFailResult("添加失败");
}
} /**
* 修改
*/
@RequestMapping("/update")
public Result update(@RequestBody Picture picture, @TokenToUser AdminUser loginUser) {
if (loginUser == null) {
return ResultGenerator.genErrorResult(Constants.RESULT_CODE_NOT_LOGIN, "未登录!");
}
if (null == picture.getId() || StringUtils.isEmpty(picture.getPath()) || StringUtils.isEmpty(picture.getRemark())) {
return ResultGenerator.genErrorResult(Constants.RESULT_CODE_PARAM_ERROR, "参数异常!");
}
Picture tempPicture = pictureService.queryObject(picture.getId());
if (tempPicture == null) {
return ResultGenerator.genErrorResult(Constants.RESULT_CODE_PARAM_ERROR, "参数异常!");
}
if (pictureService.update(picture) > 0) {
return ResultGenerator.genSuccessResult();
} else {
return ResultGenerator.genFailResult("修改失败");
}
} /**
* 删除
*/
@RequestMapping("/delete")
public Result delete(@RequestBody Integer[] ids, @TokenToUser AdminUser loginUser) {
if (loginUser == null) {
return ResultGenerator.genErrorResult(Constants.RESULT_CODE_NOT_LOGIN, "未登录!");
}
if (ids.length < 1) {
return ResultGenerator.genErrorResult(Constants.RESULT_CODE_PARAM_ERROR, "参数异常!");
}
if (pictureService.deleteBatch(ids) > 0) {
return ResultGenerator.genSuccessResult();
} else {
return ResultGenerator.genFailResult("删除失败");
}
}

删除功能

删除功能通常分为逻辑删除和物理删除,逻辑删除是名义上的删除,而物理删除是真正的删除。

举个简单的例子,仓库的货架上堆放着各种商品,管理员在取商品的时候不是直接去货架找,而是通过货架商品单上记录的信息去找,按照单子上面记载的位置再去找商品。逻辑删除相当于把货架商品单上的记录用线划掉,表明这件商品已经卖出去了或者已经处理掉了,可实际上物品并没有被拿走还是放在库里,只不过是被标记为 “ 已处理 ” 即不能再进行出库操作了。而物理删除则是把仓库货架上的商品实实在在扔掉或者处理掉了,即货架中根本没有这件商品了。

两者在编码实现时的区别是使用 delete 语句还是 update 语句。

比如,物理删除的实现代码为:

 delete from tb_xxx where id = 10 

而逻辑删除的实现代码为:

 update tb_xxx set is_deleted=1 where id = 10 

在实际开发过程中,删除数据一定要慎重,对于重要的数据,最好不要轻易物理删除(即直接删除),在必要的情况下可以使用逻辑删除的方法,即设置一个删除标志的列属性表示逻辑删除,比如本项目中使用的就是 is_deleted 字段来标识记录是否被删除。

具体的js代码

 $(function () {
//隐藏弹框
$('#pictureModal').modal('hide');
//隐藏错误提示框
$('.alert-danger').css("display", "none"); //modal绑定hide事件
$('#pictureModal').on('hide.bs.modal', function () {
reset();
});
$("#jqGrid").jqGrid({
url: 'pictures/list',
datatype: "json",
colModel: [
{label: 'id', name: 'id', index: 'id', width: 50, sortable: false, hidden: true, key: true},
{label: '图片预览', name: 'path', index: 'path', sortable: false, width: 105, formatter: imgFormatter},
{label: '图片备注', name: 'remark', index: 'remark', sortable: false, width: 105},
{label: '添加时间', name: 'createTime', index: 'createTime', sortable: true, width: 80}
],
height: 385,
rowNum: 10,
rowList: [10, 30, 50],
styleUI: 'Bootstrap',
loadtext: '信息读取中...',
rownumbers: true,
rownumWidth: 25,
autowidth: true,
multiselect: true,
pager: "#jqGridPager",
jsonReader: {
root: "data.list",
page: "data.currPage",
total: "data.totalPage",
records: "data.totalCount"
},
prmNames: {
page: "page",
rows: "limit",
order: "order"
},
gridComplete: function () {
//隐藏grid底部滚动条
$("#jqGrid").closest(".ui-jqgrid-bdiv").css({"overflow-x": "hidden"});
}
}); //预览图的设置
function imgFormatter(cellvalue) {
return "<a href='" + cellvalue + "'> <img src='" + cellvalue + "' height=\"120\" width=\"135\" alt='SSM'/></a>";
} // 上传前的预览图展示和上传照片步骤
new AjaxUpload('#upload', {
action: 'images',
name: 'file',
autoSubmit: true,
responseType: "json",
onSubmit: function (file, extension) {
if (!(extension && /^(jpg|jpeg|png|gif)$/.test(extension.toLowerCase()))) {
alert('只支持jpg、png、gif格式的图片!', {
icon: "error",
});
return false;
}
},
onComplete: function (file, r) {
if (r.resultCode == 200) {
alert("上传成功");
$("#picturePath").val(r.data);
$("#img").attr("src", r.data);
console.log("r.data="+r.data);
$("#img").attr("style", "width: 100px;height: 100px;display:block;");
return false;
} else {
alert(r.message);
}
}
});
}); //绑定modal上的保存按钮
$('#saveButton').click(function () {
//验证数据
if (validObject()) {
//一切正常后发送网络请求
//ajax
var id = $("#pictureId").val();
var picturePath = $("#picturePath").val();
var pictureRemark = $("#pictureRemark").val();
var data = {"path": picturePath, "remark": pictureRemark};
var url = 'pictures/save';
//id>0表示编辑操作
if (id > 0) {
data = {"id": id, "path": picturePath, "remark": pictureRemark};
url = 'pictures/update';
}
$.ajax({
type: 'POST',//方法类型
dataType: "json",//预期服务器返回的数据类型
url: url,//url
contentType: "application/json; charset=utf-8",
beforeSend: function (request) {
//设置header值
request.setRequestHeader("token", getCookie("token"));
},
data: JSON.stringify(data),
success: function (result) {
checkResultCode(result.resultCode);
if (result.resultCode == 200) {
$('#pictureModal').modal('hide');
alert("保存成功");
reload();
}
else {
$('#pictureModal').modal('hide');
alert("保存失败");
}
;
},
error: function () {
alert("操作失败");
}
});
}
}); function pictureAdd() {
reset();
$('.modal-title').html('图片添加');
$('#pictureModal').modal('show');
} function pictureEdit() {
reset();
$('.modal-title').html('修改图片'); var id = getSelectedRow();
if (id == null) {
return;
}
//请求数据
$.ajax({
type: "GET",
url: "pictures/info/" + id,
contentType: "application/json",
beforeSend: function (request) {
//设置header值
request.setRequestHeader("token", getCookie("token"));
},
success: function (r) {
checkResultCode(r.resultCode);
if (r.resultCode == 200 && r.data != null) {
//填充数据至modal
console.log(r.data);
$('#pictureId').val(r.data.id);
$("#img").attr("src", r.data.path);
$("#img").attr("style", "width: 100px;height: 100px;display:block;");
$('#picturePath').val(r.data.path);
$('#pictureRemark').val(r.data.remark);
}
}
});
//显示modal
$('#pictureModal').modal('show');
} /**
* 数据验证
*/
function validObject() {
var picturePath = $('#picturePath').val();
if (isNull(picturePath)) {
showErrorInfo("图片不能为空!");
return false;
}
var pictureRemark = $('#pictureRemark').val();
if (isNull(pictureRemark)) {
showErrorInfo("备注信息不能为空!");
return false;
}
if (!validLength(pictureRemark, 150)) {
showErrorInfo("备注信息长度不能大于150!");
return false;
}
if (!validLength(picturePath, 120)) {
showErrorInfo("图片上传有误!");
return false;
}
return true;
} /**
* 重置
*/
function reset() {
//隐藏错误提示框
$('.alert-danger').css("display", "none");
//清空数据
$('#pictureId').val(0);
$('#picturePath').val('');
$('#pictureRemark').val('');
$("#img").attr("style", "display:none;");
} function deletePicture() {
var ids = getSelectedRows();
if (ids == null) {
return;
}
$.ajax({
type: "POST",
url: "pictures/delete",
contentType: "application/json",
beforeSend: function (request) {
//设置header值
request.setRequestHeader("token", getCookie("token"));
},
data: JSON.stringify(ids),
success: function (r) {
checkResultCode(r.resultCode);
if (r.resultCode == 200) {
alert("删除成功");
$("#jqGrid").trigger("reloadGrid");
} else {
alert(r.message);
}
}
});
} /**
* jqGrid重新加载
*/
function reload() {
reset();
var page = $("#jqGrid").jqGrid('getGridParam', 'page');
$("#jqGrid").jqGrid('setGridParam', {
page: page
}).trigger("reloadGrid");
}

SSM实现图片上传管理操作的更多相关文章

  1. ssm实现图片上传

    在使用ssm完成前后端对接时,总免不了前台传过来的文件问题,而html中的<input>框直接使用时,往往会获取不到路径,今天在完成图片上传后的来做个总结 首先,前台页面 <!DOC ...

  2. Asp.Net Core Web Api图片上传(一)集成MongoDB存储实例教程

    Asp.Net Core Web Api图片上传及MongoDB存储实例教程(一) 图片或者文件上传相信大家在开发中应该都会用到吧,有的时候还要对图片生成缩略图.那么如何在Asp.Net Core W ...

  3. vue+axios实现移动端图片上传

    在利用vue做一些H5页面时,或多或少会遇到有图片上传的操作,主要是运用html5里面的input[type=file]来实现,传递到后端的数据是以二进制的格式传递,所以上传图片的请求与普通的请求稍微 ...

  4. thinkphp3.2.3 ueditor1.4.3 图片上传操作,在线删除上传图片功能。

    最近弄一个图片 上传,可是用ueditor 自带的上传,如果不配置的话,上传的目录不在自己的项目中. 在网上找了好多,可是都是底版本的,新版本的还真是找到了一个,ueditor-thinkphp 这个 ...

  5. php相册功能实现(包含php图片上传,后台管理,浏览和删除)教程例子

    相册功能实现(包含php图片上传,后台管理,浏览和删除)教程例子包括五个部分: 一.相册首页 <html> <head> <meta charset="utf- ...

  6. ssm使用Ajax的formData进行异步图片上传返回图片路径,并限制格式和大小

    之前整理过SSM的文件上传,这次直接用代码了. 前台页面和js //form表单 <form id= "uploadForm" enctype="multipart ...

  7. ssm框架实现图片上传显示并保存地址到数据库

    本案例是通过springmvc+spring+mybatis框架以商品上传为例,实现的图片上传功能,并把图片的地址保存到数据库并在前台显示上传的图片. 本项目是使用maven搭建的项目,首先看下项目结 ...

  8. 在线HTML文档编辑器使用入门之图片上传与图片管理的实现

    在线HTML文档编辑器使用入门之图片上传与图片管理的实现: 官方网址: http://kindeditor.net/demo.php 开发步骤: 1.开发中只需要导入选中的文件(通常在 webapp ...

  9. php利用七牛云的对象存储完成图片上传-高效管理图片

    在搭建个人博客时,大家都会买一台云服务器.可是图片的存放一直是一个问题,冷月帮大家找到一个免费的第三方平台对象存储-七牛云.大家可以把图片上传到七牛云的对象存储,大大节约服务器的压力. 首先,大家在使 ...

随机推荐

  1. 474. Ones and Zeroes

    In the computer world, use restricted resource you have to generate maximum benefit is what we alway ...

  2. hadoop1.0.4运行程序出现“Java heap Space”错误

    根据虾皮博客中教程,成功搭建了一个12台电脑的Hadoop云平台,而且成功运行了软件自带的wordcount程序,处理10M数据. 但是当程序处理40M时候,却出错了.出错提示“Java Heap S ...

  3. 解决org.apache.lucene.store.AlreadyClosedException: this Directory is closed

    在Lucene中,关闭一个IndexWriter时抛出AlreadyClosedException异常: org.apache.lucene.store.AlreadyClosedException: ...

  4. mybatis pagehelper多数据源配置的坑

    我用spring boot配置了2个数据源的工程用来同步不同库的数据,发现如果配置成如下格式报错 #分页配置pagehelper: helper-dialect: mysql reasonable: ...

  5. Windows系统下如何在cmd命令窗口中切换Python2.7和Python3.6

    针对在同一系统下我们可能安装多个版本的Python,毕竟Python2.7与Python3.6还是有不同的需求,但是在用Cmd命令窗口是我们可能默认的系统变量环境是其中一个版本,当我们需要在cmd命令 ...

  6. vue封装插件并发布到npm上

    vue封装插件并发布到npm上 项目初始化 首先,要创建项目,封装vue的插件用webpack-simple很合适,vue init webpack-simple 项目名称此命令创建我们的项目的目录, ...

  7. C#生成验证码类

    using System;using System.Collections.Generic;using System.Drawing;using System.Drawing.Drawing2D;us ...

  8. Google Spanner vs Amazon Aurora: Who’ll Get the Enterprise?

    https://www.clustrix.com/bettersql/spanner-vs-aurora/ Google Spanner versus Amazon Aurora In July 20 ...

  9. Gradle学习系列(二)

    AS的逐渐成熟和完善,已有越来越多的项目开发都开始转向AS了,必然的对Gradel的认识和使用是很有必要了.我们已经知道 Gradle 是用来架构 Java项目了,对于Android Project来 ...

  10. IT人生的价值和意义 感觉真的有了

     为了做新闻APP,我居然短短一个月利用业余时间做了: 一个通用新闻采集器. 一个新闻后台审核网站. 一个通用采集器下载网站. 一个新闻微网站. 一个新闻APP, 而且还给新闻微网站和新闻 APP练就 ...