網(wǎng)站建設(shè)教程app今日新聞事件
前言
如果你的前后端分離項目采用SpringBoot3+Vue3+Element Plus,且在沒有OSS(對象存儲)的情況下,使用mysql讀寫圖片(可能不限于圖片,待測試)。
耗時三天,在踩了無數(shù)雷后,終于完成本功能。為你呈上。
本文完成功能:
- 前端采用Element發(fā)起上傳圖片請求,后端接收并將其存儲到mysql。
- 后端相應(yīng)圖片base64數(shù)據(jù),前端接收并渲染到頁面。
1.前端上傳到數(shù)據(jù)庫
1.1前端上傳圖片
<el-form-item label="寵物照片" prop="pictureId">
<el-upload v-model="form.pictureId" :auto-upload="false" :action="''" :show-file-list="true" :on-change="handleAvatarChangeIcon"><el-button type="primary">選取文件</el-button>
</el-upload>
</el-form-item>
參數(shù):
:auto-upload 是否自動上傳
:action 自動上傳的請求路徑
:show-file-list 顯示已上傳的圖片列表
:on-change 選中文件觸發(fā)的change事件
自動上傳與否都不影響,這里主要是判斷一下圖片的大小、后綴名。如下:
const handleAvatarChangeIcon = (file) => {// 限制文件后綴名const isJPG = file.raw.type === 'image/jpeg'const isPNG = file.raw.type === 'image/png'// 限制上傳文件的大小const isLt5M = file.raw.size / 1024 / 1024 < 5if (!isPNG && !isJPG) {ElMessage.error('圖片只能是 JPG/PNG 格式')return false} else if (!isLt5M) {ElMessage.error('圖片應(yīng)在5MB以內(nèi)')return false} else {// 發(fā)起請求let param = new FormData();// 文件為form data格式param.append("file", file.raw);post('/api/file/upload', param, (res) => {ElMessage.success('上傳成功');// 返回值為圖片idform.pictureId = res})}
}
1.2后端接收并保存數(shù)據(jù)庫
controller
@RestController
@RequestMapping("/api/file")
public class FileController {@Resourceprivate FileService fileService;@PostMapping("/upload")public RestBean<String> upload(@RequestParam MultipartFile file) {Integer res = fileService.upload(file);return RestBean.success(String.valueOf(res));}
}
serviceImpl
@Service
public class FileServiceImpl implements FileService {@Resourceprivate FileMapper fileMapper;/*** 文件上傳到數(shù)據(jù)庫*/@Overridepublic Integer upload(MultipartFile file) throws IOException {// 獲取文件原始名String originalFilename = file.getOriginalFilename();// 獲取文件后綴名String endName = "png";if (originalFilename != null) {endName = originalFilename.substring(originalFilename.lastIndexOf("."));}// 拼接文件名String filename = UUID.randomUUID() + endName;Integer pictureId;// 創(chuàng)建圖片對象byte[] fileBytes = file.getBytes();Picture picture = new Picture();picture.setName(filename);picture.setPicData(fileBytes);// 上傳數(shù)據(jù)庫fileMapper.upload(picture);pictureId = picture.getId();// 返回圖片idreturn pictureId;}
}
mapper.xml
<mapper namespace="com.ycb.mapper.FileMapper"><insert id="upload" useGeneratedKeys="true" keyProperty="id">insert into `pet-adoption`.picture(name, pic_data)value (#{name}, #{picData})</insert>
</mapper>
數(shù)據(jù)庫設(shè)計
2.前端從數(shù)據(jù)庫獲取圖片并渲染
2.1后端從數(shù)據(jù)庫中獲取
entity
public class PetAndBulVO {/*** 照片*/private byte[] picData;
}
controller
如果是一個圖片數(shù)據(jù)直接封裝到實體類,很多數(shù)據(jù)就封裝成集合
@RequestMapping("/api/pet")
public class PetController {@Resourceprivate PetService petService;@GetMapping("/getAllPB")public RestBean<List<PetAndBulVO>> getAll() {List<PetAndBulVO> pets = petService.getAll();return RestBean.success(pets);}
}
serviceImpl
@Service
public class PetServiceImpl implements PetService {@Resourceprivate PetMapper petMapper;@Overridepublic List<PetAndBulVO> getAll() {return petMapper.getAll();}
}
mapper.xml
<mapper namespace="com.ycb.mapper.PetMapper"><!-- 一定要映射結(jié)果集 --><resultMap type="com.ycb.entity.vo.response.PetAndBulVO" id="petAndBulVO"><id column="pic_data" property="picData" javaType="byte[]" jdbcType="BLOB" typeHandler="org.apache.ibatis.type.BlobTypeHandler"/></resultMap><select id="getAll" resultMap="petAndBulVO">select *from `pet-adoption`.pet petjoin `pet-adoption`.picture p on p.id = pet.picture_id</select>
后端返回的圖片數(shù)據(jù)如下:
2.2前端接收數(shù)據(jù)并渲染
后端傳來的數(shù)據(jù)是 base64 形式的,需要解碼
// 解碼
const base64ToUrl = (base64) => {return 'data:image/png;base64,' + base64
}// 獲取數(shù)據(jù)
get('/api/pet/getAllPB', (data) => {for (let i = 0; i < data.length; i++) {data[i].picData = base64ToUrl(data[i].picData)}pBList.value = data
}, (err) => {ElMessage.error(err)
})
解碼后的圖片數(shù)據(jù)如下:
渲染是大坑!一定要 v-bind: 綁定 src
// v-for循環(huán)獲取picData, v-for="(pb) in pBList"
<el-image v-bind:src="pb.picData"/>
《林克可愛圖》
寫在最后
雖然可以實現(xiàn)僅用mysql就能完成圖片讀寫,但其性能堪憂。
很難,但貴在堅持。