前言
在 Web 專案中多多少少都會遇到要把表單資料傳至後端儲存的功能,可是外加圖片檔案後就變得不那麼單純,這邊想分享是把表單資料加上圖片檔案,放在同一個物件內傳送到後端做保存。
問題敘述
把網頁中的彈跳視窗內的表單與圖片檔案,以 base64 格式一起傳至後端,前端以虛擬靜態路徑顯示圖片。
效果展示
邏輯思路
前端取得表單和圖片的 base64 格式,傳至後端。
後端取得表單內容和解析 base64 格式,轉換成圖片jpg 格式。
把圖片保存至本機,製作圖片的虛擬靜態路徑,保存在資料庫裡。
Vue HTML
這裡是使用 element UI 的上傳檔案模板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <el-form-item label ="上傳照片" prop ="img" label-width ="100px" > <el-input v-model ="editForm.avatar" v-if ="false" > </el-input > <el-upload class ="upload-demo" ref ="upload" action accept ="image/jpeg, image/png" :http-request ="httpRequest" :before-upload ="beforeUpload" :on-exceed ="handleExceed" :on-change ="handleChange" :limit ="1" :data ="editForm" > <el-button slot ="trigger" size ="small" type ="primary" > 選取照片</el-button > <div slot ="tip" class ="el-upload__tip" > 只能上傳 jepg、png 檔案,且不超過 5 MB</div > </el-upload > </el-form-item >
JavaScript
這裡是前端的邏輯處理,把圖片檔案轉成 Base64 格式。
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 data ( ) { return { fileList : [], }; }, methods : { httpRequest (option ) { this .fileList .push (option) }, beforeUpload (file ) { let fileSize = file.size ; let fileName = file.name ; const FIVE_M = 5 * 1024 * 1024 ; if (fileSize > FIVE_M ) { this .$message .error ("最大上傳 5 MB" ) return false } if (!/\.(jpg|jpeg|png|JPG|PNG)$/ .test (fileName)) { this .$message .error ("只能上傳 jpg、png 格式!" ) return false } return true }, handleExceed ( ) { this .$message({ type : 'error' , message : '最多支持 1 個照片上傳' }) }, handleChange (file ) { this .getBase64 (file.raw ).then (res => { this .editForm .avatar = res; }); }, getBase64 (file ) { return new Promise (function (resolve, reject ) { let imgResult = "" ; let reader = new FileReader (); reader.readAsDataURL (file); reader.onload = function ( ) { imgResult = reader.result ; }; reader.onerror = function (error ) { reject (error); }; reader.onloadend = function ( ) { resolve (imgResult); }; }); }, },
Spring Boot Entity
這裡繼承了一個我專案中的父類實體,與這個教學毫無關係,這裡想表達我的實體屬性是 avatar,其他屬性略。
1 2 3 4 @Data public class ManageUser extends BaseEntity { private String avatar; }
Config
這邊很重要,瀏覽器是看不懂你本機電腦裡的圖片路徑的,要瀏覽器顯示你的圖片的話,一定要配置這個在 Spring 裡。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Configuration public class MyWebConfigurer extends WebMvcConfigurationSupport { @Override public void addResourceHandlers (ResourceHandlerRegistry registry) { registry.addResourceHandler("/api/file/**" ).addResourceLocations("file:" + "D:/xxx/src/main/resources/static/img/" ); super .addResourceHandlers(registry); } }
Utils
工具類來處理前端傳來的物件,將 base64 格式轉換成 jpg 圖片格式,並保存圖片在本機,把虛擬靜態路徑回傳。
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 @Slf4j @Component public class ImgUtils { public static String getUUID () { String uuid = UUID.randomUUID().toString().replaceAll("-" , "" ); return uuid; } public static String getAvatarBase64 (String base64) { String imgUrl = ImgUtils.handleUploadPicture(base64); log.info("圖片url:" + imgUrl); return imgUrl; } public static String handleUploadPicture (String base64) { String fileUrl = null ; String lastFilePath; String uploadFolder = "D:/xxx/src/main/resources/static/img/" ; String apiUrl = "http://localhost:8081/api/file/" ; String suffix = null ; if (base64.startsWith("data:image/jpg" )) { suffix = ".jpg" ; } else if (base64.startsWith("data:image/jpeg" )) { suffix = ".jpeg" ; } else if (base64.startsWith("data:image/png" )) { suffix = ".png" ; } String newFileName = ImgUtils.getUUID() + suffix; File folder = new File (uploadFolder); if (!folder.exists()) { folder.mkdirs(); } FileOutputStream out = null ; final BASE64Decoder decoder = new BASE64Decoder (); try { lastFilePath = uploadFolder + File.separator + newFileName; out = new FileOutputStream (lastFilePath); byte [] decoderBytes = decoder.decodeBuffer(base64.split("," )[1 ]); out.write(decoderBytes); fileUrl = apiUrl + newFileName; log.info("虛擬靜態路徑:" + fileUrl); } catch (Exception e) { e.printStackTrace(); } finally { if (out != null ) { try { out.flush(); } catch (IOException e) { e.printStackTrace(); } try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } return fileUrl; } }
Service
Spring Boot 業務的介面類 (由 Controller 來引用)
1 2 3 4 5 6 public interface ManageUserService extends IService <ManageUser> { ManageUser UserAvatarInfoSave (ManageUser manageUser) ; }
ServiceImpl
Spring Boot 業務的實現類 (由業務的介面類來管理) 這裡主要的工作是 Controller 收到前端的 manageUser 物件了,現在要把 manageUser 物件裡的 Avatar (Base64格式),拿出來給上面寫好的工具類處理,解析轉換完後把他丟回 manageUser 物件裡。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Slf4j @Service public class ManageUserServiceImpl extends ServiceImpl <ManageUserMapper, ManageUser> implements ManageUserService { @Autowired ImgUtils imgUtils; @Override public ManageUser UserAvatarInfoSave (ManageUser manageUser) { String avatar = manageUser.getAvatar(); String imgUrl = imgUtils.getAvatarBase64(avatar); manageUser.setAvatar(imgUrl); return manageUser; } }
Controller
Controller 接收到前端傳來的資料物件後,使用業務的介面類,來引用業務的實現類,去調用工具類處理 Avatar,處理完在把整個 manageUser 物件存進資料庫裡。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @RestController @RequestMapping("/manage/user") public class ManageUserController extends BaseController { @PostMapping("/save") public Result save (@Validated @RequestBody ManageUser manageUser) { manageUserService.UserAvatarInfoSave(manageUser); manageUserService.save(manageUser); return Result.succ(manageUser); } }
結語
這邊盡可能提供詳細的方法給各位參考,筆者也還在努力學習中,寫的並不是非常好,可能有哪些地方邏輯可以再加強或者再優化,也不吝嗇被指教,感謝各位的觀看。