Vue+SpringBoot+OSS+element实现图片上传服务器+图片展示墙
利用Vue、SpringBoot、OSS、element实现了将图片上传到了阿里云的OSS数据库上,利用自制图片墙将上传的图片进行显示,实现了图片的上传和“下载”的实现,这里的下载指的是图片的读取,没有真正实现下载!
1、技术选型:
(1)Vue(前端)
(2)Element(上传图片组件)
(3)SpringBoot(后端)
(4)Mybatis(数据库操作)
(3)OSS(阿里云存储服务器)
2、实现效果概述:
(1)图片展示墙(做了一个简单的,没有太花里胡哨)
(2)上传页面
3、项目概述:
注意这是一个练习项目,练习项目,练习项目,可能存在一些小问题!
简单来说,
=(1)图片上传逻辑:=
前端通过elementUI的上传图片的组件将图片进行上传,后端利用SpringBoot写的后端逻辑接受了前端穿过的图片,首先将图片上传到阿里云的OSS对象存储系统上,然后返回图片的地址存储到数据库。
=(2)图片展示墙逻辑:=
通过查询数据库的图片地址返回给前端,然后前端通过for循环,利用img标签对图片进行循环遍历然后显示!
项目简单,易于实现,全代码黏贴!!!!!!
4、图片上传
(1)前端项目结构:
(2)ImagesUpload.vue
=补充:这里主要是借助了elementUI的上传图片组件!!!!
这里只需要将action中的地址改成自己的后端请求地址就可以!!!=
<template>
<div>
<el-upload
action="http://localhost:8086/images/upload"
list-type="picture-card"
:on-preview="handlePictureCardPreview"
:on-remove="handleRemove">
<i class="el-icon-plus"></i>
</el-upload>
</div>
</template>
<el-dialog :visible.sync="dialogVisible">
<img width="100%" :src="dialogImageUrl" alt="">
</el-dialog>
<script>
export default {
name: "images-upload",
data() {
return {
dialogImageUrl: '',
dialogVisible: false
};
},
methods: {
handleRemove(file, fileList) {
console.log(file, fileList);
},
handlePictureCardPreview(file) {
this.dialogImageUrl = file.url;
this.dialogVisible = true;
}
}
}
</script>
<style scoped>
</style>
==前端逻辑完成,开始后端逻辑=
(3)安装阿里云OSS所需依赖:
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>2.8.3</version>
</dependency>
(4)OSS工具类封装:
=这里根据自己的实际情况合理选择域名,访问用户,秘钥…=
package com.blog.util;
/**
* @author
* @version 1.0
* @description TODO
* @date 2023/3/12 8:28
*/
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.util.Date;
import java.util.Random;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PutObjectResult;
public class OSSClientUtil {
/**
* log日志
*/
public static final Logger logger = LoggerFactory.getLogger(OSSClientUtil.class);
/**
* 访问域名
*/
private String endpoint = "oss-cn-beijing.aliyuncs.com";
/**
* accessKey访问秘钥
* 访问用户
*/
private String accessKeyId = "########";
/**
* 密钥
*/
private String accessKeySecret = "################";
/**
* 空间 名称
*/
private String bucketName = "############";
/**
* 文件存储目录
*/
private String filedir = "###########/";
private OSSClient ossClient;
public OSSClientUtil() {
ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);
}
/**
* 初始化
*/
public void init() {
ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);
}
/**
* 销毁
*/
public void destory() {
ossClient.shutdown();
}
/**
* 上传图片
*
* @param url
* @throws
*/
public void uploadImg2Oss(String url) throws IOException {
File fileOnServer = new File(url);
FileInputStream fin;
try {
fin = new FileInputStream(fileOnServer);
String[] split = url.split("/");
this.uploadFile2OSS(fin, split[split.length - 1]);
} catch (FileNotFoundException e) {
throw new IOException("图片上传失败");
}
}
public String uploadImg2Oss(MultipartFile file) throws IOException {
if (file.getSize() > 10 * 1024 * 1024) {
throw new IOException("上传图片大小不能超过10M!");
}
String originalFilename = file.getOriginalFilename();
String substring = originalFilename.substring(originalFilename.lastIndexOf(".")).toLowerCase();
Random random = new Random();
String name = random.nextInt(10000) + System.currentTimeMillis() + substring;
try {
InputStream inputStream = file.getInputStream();
this.uploadFile2OSS(inputStream, name);
return name;
} catch (Exception e) {
throw new IOException("图片上传失败");
}
}
/**
* 获得图片路径
*
* @param fileUrl
* @return
*/
public String getImgUrl(String fileUrl) {
System.out.println(fileUrl);
if (!StringUtils.isEmpty(fileUrl)) {
String[] split = fileUrl.split("/");
return this.getUrl(this.filedir + split[split.length - 1]);
}
return "" ;
}
/**
* 上传到OSS服务器 如果同名文件会覆盖服务器上的
*
* @param instream
* 文件流
* @param fileName
* 文件名称 包括后缀名
* @return 出错返回"" ,唯一MD5数字签名
*/
public String uploadFile2OSS(InputStream instream, String fileName) {
String ret = "";
try {
// 创建上传Object的Metadata
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(instream.available());
objectMetadata.setCacheControl("no-cache");
objectMetadata.setHeader("Pragma", "no-cache");
objectMetadata.setContentType(getcontentType(fileName.substring(fileName.lastIndexOf("."))));
objectMetadata.setContentDisposition("inline;filename=" + fileName);
// 上传文件
PutObjectResult putResult = ossClient.putObject(bucketName, filedir + fileName, instream, objectMetadata);
ret = putResult.getETag();
} catch (IOException e) {
logger.error(e.getMessage(), e);
} finally {
try {
if (instream !=null ) {
instream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return ret;
}
/**
* Description: 判断OSS服务文件上传时文件的contentType
*
* @param
*
* @return String
*/
public static String getcontentType(String filenameExtension) {
if (filenameExtension.equalsIgnoreCase("bmp")) {
return "image/bmp";
}
if (filenameExtension.equalsIgnoreCase("gif")) {
return "image/gif";
}
if (filenameExtension.equalsIgnoreCase("jpeg") || filenameExtension.equalsIgnoreCase("jpg")
|| filenameExtension.equalsIgnoreCase("png")) {
return "image/jpeg";
}
if (filenameExtension.equalsIgnoreCase("html")) {
return "text/html";
}
if (filenameExtension.equalsIgnoreCase("txt")) {
return "text/plain";
}
if (filenameExtension.equalsIgnoreCase("vsd")) {
return "application/vnd.visio";
}
if (filenameExtension.equalsIgnoreCase("pptx") || filenameExtension.equalsIgnoreCase("ppt")) {
return "application/vnd.ms-powerpoint";
}
if (filenameExtension.equalsIgnoreCase("docx") || filenameExtension.equalsIgnoreCase("doc")) {
return "application/msword";
}
if (filenameExtension.equalsIgnoreCase("xml")) {
return "text/xml";
}
return "image/jpeg";
}
/**
* 获得url链接
*
* @param key
* @return
*/
public String getUrl(String key) {
// 设置URL过期时间为10年 3600l* 1000*24*365*10
Date expiration = new Date(System.currentTimeMillis() + 3600L * 1000 * 24 * 365 * 10);
// 生成URL
// URL url = ossClient.generatePresignedUrl(bucketName, key, expiration);
String url = "https://"+bucketName+"."+endpoint+"/"+key;
if (url != null) {
return url.toString();
}
return "";
}
}
(5)Controller层:
package com.blog.controller.Vue;
import com.blog.pojo.Images;
import com.blog.service.ImagesService;
import com.blog.util.FileUtil;
import com.blog.util.OSSClientUtil;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* @author
* @version 1.0
* @description TODO
* @date 2023/3/11 10:39
*/
@RestController
@RequestMapping("/images")
public class UploadController {
@Autowired
ImagesService imagesService;
@PostMapping("/upload")
public Map<String, Object> fileupload(MultipartFile file, HttpServletRequest req) throws IOException {
if (file == null || file.getSize() <= 0) {
throw new IOException("file不能为空");
}
//获取文件的大小,单位/KB
long size = file.getSize();
OSSClientUtil ossClient=new OSSClientUtil();
//将文件上传
String name = ossClient.uploadImg2Oss(file);
//获取文件的URl地址 以便前台 显示
String imgUrl = ossClient.getImgUrl(name);
HashMap<String, Object> map=new HashMap<>();
//文件存储的路径
map.put("name", imgUrl);
Images images = new Images();
images.setImg(imgUrl);
System.out.println("添加数据");
imagesService.insert(images);
return map ;
}
}
(6)Service层:
package com.blog.service;
import com.blog.pojo.Images;
import java.util.List;
public interface ImagesService {
int insert(Images images);
List<Images> getImages();
}
(7)ServiceImpl:
package com.blog.service.impl;
import com.blog.dao.ImagesDao;
import com.blog.pojo.Images;
import com.blog.service.ImagesService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @author
* @version 1.0
* @description TODO
* @date 2023/3/12 10:38
*/
@Service
public class ImagesServiceImpl implements ImagesService {
@Autowired
ImagesDao imagesDao;
@Override
public int insert(Images images) {
return imagesDao.insert(images);
}
@Override
public List<Images> getImages() {
return imagesDao.getImages();
}
}
(8)Dao层:
package com.blog.dao;
import com.blog.pojo.Images;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@Mapper
@Repository
public interface ImagesDao {
int insert(Images images);
List<Images> getImages();
}
(9)xml层:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.blog.dao.ImagesDao">
<resultMap id="images" type="Images">
<id property="id" column="id"/>
<result property="img" column="img"/>
</resultMap>
<insert id="insert" parameterType="Images">
insert into t_images (img)
values (#{img});
</insert>
<select id="getImages" resultMap="images">
select * from t_images
</select>
</mapper>
(10)实体类:
package com.blog.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author
* @version 1.0
* @description TODO
* @date 2023/3/12 10:33
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Images {
private Integer id;
private String img;
}
5、照片墙功能:
(1)前端项目结构:
(2)Waterfall组件(主要是通过一个瀑布流图片展示组件来实现)
<template>
<div class="m-waterfall-wrap" :style="`background: ${backgroundColor}; width: ${width}px; padding: ${columnGap}px; column-count: ${columnCount}; column-gap: ${columnGap}px;`">
<div class="m-img" :style="`margin-bottom: ${columnGap}px;`" v-for="(item, index) in imageData" :key="index">
<img class="u-img" :src="item.img" :title="item.title" :alt="item.title" />
</div>
</div>
</template>
<script>
/*
纯CSS,实现简单,但图片顺序是每列从上往下排列
*/
export default {
name: 'Waterfall',
props: {
imageData: { // 瀑布流的图片数组
type: Array,
required: true,
default: () => {
return []
}
},
columnCount: { // 瀑布流要划分的列数
type: Number,
default: 3
},
columnGap: { // 瀑布流各列之间的间隙
type: Number,
default: 30
},
totalWidth: { // 瀑布流区域的总宽度
type: Number,
default: 1200
},
backgroundColor: { // 瀑布流区域背景填充色
type: String,
default: '#F2F4F8'
}
},
computed: {
width () {
return this.totalWidth - 2 * this.columnGap
}
}
}
</script>
<style lang="less" scoped>
.m-waterfall-wrap {
.m-img {
.u-img {
width: 100%;
vertical-align: bottom;
}
}
}
</style>
(3)ImagesShow.vue(照片墙显示页面,借助于上一个瀑布流图片展示组件)
=①在这里需要注意路径问题,这里./代表了本项目所在的目录,在引入组件地址的时候,要根据自己项目的实际路径合理改变!=
<template>
<div style="background-color: #fffef9">
<p style="text-align: center;font-size: 24px;font-family: 'Adobe 黑体 Std R'" >图片展示墙</p>
<Waterfall
:imageData="images"
:columnCount="3"
:columnGap="30"
:totalWidth="totalWidth"
style="margin: 0 auto"
backgroundColor="#f6f5ec" />
</div>
</template>
<script>
import Waterfall from '../../components/Waterfall.vue'
import {list} from '../../api/images';
export default {
name: "images-show",
components:{
Waterfall
},
data(){
return{
//screenWidth: document.body.clientWidth, // 屏幕宽度
totalWidth: document.body.clientWidth, // 屏幕高度
images:[],
}
},
mounted() {
window.screenWidth = document.body.clientWidth;
window.screenHeight = document.body.clientHeight;
this.screenWidth = window.screenWidth;
this.screenHeight = window.screenHeight;
console.log(window.screenWidth);
console.log(window.screenHeight);
console.log(this.screenWidth);
console.log(this.screenHeight);
},
created(){
this.test();
},
methods:{
test(){
console.log("测试")
list().then(res=>{
this.images = res.data;
console.log(res)
})
},
}
}
</script>
<style scoped>
</style>
(4)images.js
import request from '@/request/request'
export function list() {
return request({
url: '/imagesList/list',
method: 'get',
})
}
=配置到这里,那么前端核心的逻辑已经写完了=
(5)ImagesController
package com.blog.controller.Vue;
import com.blog.pojo.Images;
import com.blog.service.ImagesService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @author
* @version 1.0
* @description TODO
* @date 2023/3/12 11:00
*/
@RestController
@RequestMapping("/imagesList")
public class ImagesController {
@Autowired
ImagesService imagesService;
@GetMapping("/list")
public List<Images> getImagesList(){
List<Images> images = imagesService.getImages();
return images;
}
}
(6)关于service层、Dao层、实体类都在图片上传的ImagesService里,在这里我就不重复添加了!!!
6、总结:
(1)利用了vue、element组件搭建了前端
(2)利用SpringBoot、Mybatis实现了后端
(3)借助OSS组件将图片上传到了阿里云OSS
7、补充:
如果你对阿里云的OSS不太熟悉,那么给你推荐几篇博客,加速理解。
=(1)阿里云OSS注册及功能详解:=
https://blog.csdn.net/weixin_52851967/article/details/126924361
=(2)SpringBoot整合阿里云OSS云存储=
https://blog.csdn.net/qq_43021813/article/details/82665394
=(3)可能碰到的问题,以我为例,我创建的是私有Bucket,所以只有带着秘钥的时候才能访问,只根据图片路径而不带秘钥是无法访问的,这篇博客包含了我遇到的一些问题!=
https://blog.csdn.net/H_Q_Li/article/details/126471699
更多推荐
所有评论(0)