本地图片上传
v-md-editor并没有给我们实现在editor中插入本地图片,这需要我们自己手动实现,最近两天考虑多种情况,还是决定采取很经典的解决方案:在数据库存储图片的从属关系以及其保存路径和图片名称,然后通过访问后端服务器来获取图片,这两天都在折腾其他事情,今天才来得及解决,下面是具体实施:
首先我们结合官方文档说明来思考方案
官方文档
下面是文档关于此处的核心代码,官方只给出了提示并没有给出方案,这需要我们自我发挥了 methods: { handleUploadImage(event, insertImage, files) { // 拿到 files 之后上传到文件服务器,然后向编辑框中插入对应的内容 console.log(files);
// 此处只做示例
insertImage({
url:
'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1269952892,3525182336&fm=26&gp=0.jpg',
desc: '七龙珠',
// width: 'auto',
// height: 'auto',
});
},
后端方面
很容易想到的是先从数据库下手,我们只需要保存图片的获取方法和从属与哪一篇文章即可,下面就是表结构:
create table images
(
id int auto_increment
primary key,
image varchar(200) not null,
dependence_id int null,
address varchar(200) not null
);
实体类
注意dependence_id是可以为空的,因为我们在编写文章时会保存草稿,此时文章并未上传数据库,但是图片一旦插入就会保存在数据库,当文章发布时才会更新dependence_id,下面是对应的实体类:
@TableName("images")
@Data
public class Image {
@TableId(type = IdType.AUTO)
private Integer id;
private String image;
private Integer dependenceId;
private String address;
}
Controller
请注意id的策略为AUTO,接下来照着模板把ImageService、ImageServiceImpl、ImageMapper、ImageController等整理一下即可,这里先列出ImageController:
@RestController
@RequestMapping("/upload/image")
public class imageController {
@Autowired
private ImageService imageService;
@Value("${MyBlog.path}")//千万别用path,这是已有的配置,不是自定义的
private String basePath;
@Value("${Service.address}")
private String baseService;
@PostMapping("/{id}")
public R<String> uploadImageOfArticle(@PathVariable("id") Integer id,@RequestBody Integer []ids){
Image image = new Image();
image.setDependenceId(id);
for(Integer idImage : ids){
image.setId(idImage);
imageService.updateById(image);
}
return R.success("图片从属关系已更新!");
}
@PostMapping
public R<String[]> uploadImage(@RequestParam("file") MultipartFile file) throws IOException {
//原始文件名
String originalFilename = file.getOriginalFilename();
assert originalFilename != null;//断定不为空
//后缀
String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
//判断是否存在目录basePath
File dir=new File(basePath);
//如果不存在就创建目录
if(!dir.exists()){
dir.mkdirs();
}
//UUID重命名文件,防止文件名称重复造成文件覆盖
String fileName= UUID.randomUUID().toString() + suffix;
System.out.println(basePath + fileName);
file.transferTo(new File(basePath + fileName));
// file.transferTo(new File("D:\\test\\test.jpg"));
Image image = new Image();
image.setImage(fileName);
image.setAddress(basePath+fileName);
imageService.save(image);
String []s={baseService+"upload/image/"+fileName, String.valueOf(image.getId())};
return R.success(s);
// return R.success("D:\\test\\test.jpg");
}
@GetMapping("/{name}")
public R<String> getById(@PathVariable("name") String name, HttpServletResponse response) throws IOException {
FileInputStream inputStream=new FileInputStream(new File(basePath + name));
ServletOutputStream outputStream = response.getOutputStream();
response.setContentType("image/jpeg");
int len;
byte[] bytes = new byte[1024];
while ((len=inputStream.read(bytes))!=-1){
outputStream.write(bytes, 0, len);
outputStream.flush();
}
outputStream.close();
inputStream.close();
return R.success("载入成功!");
}
}
分析Controller
从头到尾解释一遍此controller:@Value中的属性是对应application.yml里的值,配置如下,大家可以灵活更改:
Service:
address: http://127.0.0.1:${server.port}/
#图片保存路径
MyBlog:
path: D:\myBlogImages\
uploadImageOfArticle接口对应的是当文章提交时,更新图片的从属关系,我们要获取文章的id以及文章所包含的上传图片的数组ids,然后遍历更新一遍数组ids对应图片的从属关系
uploadImage接口对应的是图片插入时直接上传到数据库,我们只需要获取文件即可,这里采用了伪form表单提交,前端会伪造一个form表单提交,然后我们重命名图片保证其name不重复,将其保存在yml中设置的图片文件夹路径下,随后将图片的数据提交到数据库,并返回图片的获取路径和id
getById接口顾名思义,就是通过id获取图片,此处又是经典的java文件流读取,HttpServletResponse将会把图片返回;
前端方面
插入本地图片时,在官方文档提供的methods的基础上,我们将file放入FormData中,模拟表单提交即可: handleUploadImage(event, insertImage, files){ let file=new FormData(); file.append('file',files[0]);
imageLoad(file).then(res=>{
let data = res.data;
if(data.code==="100"){
this.$notify.success(this.notifyMsgSuc("图片插入成功!"));
insertImage({
url:data.data[0],
desc:files.name
});
let ids=this.localGetObject(this.articleSaveImageIds);
if(!ids) {
ids=[data.data[1]];
}else ids.push(data.data[1]);
this.localSetObject(this.articleSaveImageIds,ids);
}
}).catch(err=>this.$notify.error(this.notifyMsgErr(err.message)));
}
分析handleUploadImage
files是一个数组,而我们一次只插入一张图片,因此只需要获取files[0]即可,将其放入FormData,注意'file'要与后端接口的file保持一致,否则后端会报空指针,下面的imageLoad是我封装的ajax请求,其代码如下:
const src="/api/upload";
export function imageLoad(Data){
return axios.post(src+"/image",Data, {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
});
}
以后src将直接替代接口的根路径,就不再次呈现了,axios的第三参数为config,务必不要忘记这个配置,在请求返回成功后,执行insertImage,这是官方提供的api,随后我们将返回的图片id保存在本地存储的ids数组中,等待提交命令下达
提交操作
当点击保存时,文章模板会背保存到本地;当点击提交时会执行如下操作:
submit(){
this.Article.author=this.localGetObject(this.userObjectName).username;
articlePost(this.Article).then(res=>{
let d=res.data;
this.$notify.success(this.notifyMsgSuc(d.data));
if(d.code==="100"){
imageUploadArticle(d.data,this.localGetObject(this.articleSaveImageIds)).then(resp=>{
let im=resp.data;
if(im.code==="100"){
localStorage.removeItem(this.articleSaveImageIds);
this.$notify.success(this.notifyMsgSuc("图片从属关系已更新!"));
}else this.$notify.error(this.notifyMsgErr(im.msg))
}).catch(err=>this.$notify.error(err.message))
localStorage.removeItem(this.articleEditTemplate);//清空本地文章编辑存储
}else this.$notify.error(this.notifyMsgErr(d.msg))
}).catch(err=>this.$notify.error(this.notifyMsgErr(err.message)));
}
分析submit
提交流程为:先为Article补充信息,然后发出提交请求,提交成功后则立即为ids数组对应图片更新从属关系,然后删除本地有关的存储即可
效果展示
图片插入效果如下: