基于SpringBoot實現(xiàn)圖片上傳及圖片回顯
案例:圖書管理(SpringBoot+Thymeleaf+SpringData-JPA)
添加圖書:圖書基本信息及封面圖片的上傳及入庫
圖書詳細(xì):圖書基本信息和封面圖片顯示
- SpringData JPA 使用
- 上傳頁面的三個必須要求
- 圖片上傳接收和處理
- 資源映射(圖片回顯)
- 全局異常處理
數(shù)據(jù)庫腳本
CREATE DATABASE wdzldb` USE `wdzldb`; DROP TABLE IF EXISTS `book`; CREATE TABLE `book` ( `bookid` int(11) NOT NULL AUTO_INCREMENT, `bookName` varchar(120) DEFAULT NULL, `price` float DEFAULT NULL, `pubDate` date DEFAULT NULL, `author` varchar(20) DEFAULT NULL, `version` int(11) DEFAULT '0', `state` int(11) DEFAULT NULL, `pic` varchar(50) DEFAULT NULL, PRIMARY KEY (`bookid`) ) ENGINE=InnoDB AUTO_INCREMENT=157 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; /*Data for the table `book` */ insert into `book`(`bookid`,`bookName`,`price`,`pubDate`,`author`,`version`,`state`,`pic`) values (22,'Java實戰(zhàn)開發(fā)3',34,'2021-07-28','王磊',1,1,NULL), (53,'Java實戰(zhàn)開發(fā)666',120,'2021-07-24','諸葛亮',0,1,NULL), (61,'Java實戰(zhàn)開發(fā)1',39,'2021-07-29','王磊',0,1,NULL), (62,'Java實戰(zhàn)開發(fā)1',39,'2021-07-29','王磊',0,0,NULL), (66,'Java實戰(zhàn)開發(fā)1',39,'2021-07-29','王磊',0,0,NULL), (67,'SpringCloud微服務(wù)實戰(zhàn)',45,'2021-08-11','王帆',0,0,NULL), (68,'SPringBoot整合JDBC',56,'2021-08-11','周瑜',0,1,NULL), (70,'SpringBoot入門與提高',78,'2021-08-11','曹操',0,1,NULL), (71,'Java實戰(zhàn)開發(fā)5',100,'2021-07-23','諸葛亮',0,0,NULL), (72,'Java虛擬機(jī)深入',23,'2021-08-11','趙紫陽',0,1,NULL), (73,'深入學(xué)習(xí)Java虛擬機(jī)',69,'2021-08-05','黃蓋',0,0,NULL), (74,'JSP開發(fā)技術(shù)',34,'2021-08-12','王超',0,1,NULL)
框架搭建
先搭建基本框架,完成業(yè)務(wù)層、DAO 層、pojo 等模塊編寫,并調(diào)試通過 springdata 正常使用。
pom.xml 依賴
web 啟動器、thymeleaf 模板啟動器、springdata 啟動器及數(shù)據(jù)庫驅(qū)動等
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>配置文件
application.yml 數(shù)據(jù)源的基本信息配置、jpa 是否顯示 sql 及 下劃線等
spring:
datasource:
username: root
password: root
url: jdbc:mysql://127.0.0.1:3306/wdzldb?allowMultiQueries=true&useUnicode=true&allowPublicKeyRetrieval=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
show-sql: true
hibernate:
naming:
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
application.properties
下面多數(shù)是在使用阿里的初始化工具時,自動生成的
除了下面的上傳保存的路徑是自定義的
# 應(yīng)用名稱 spring.application.name=springboot_other # 應(yīng)用服務(wù) WEB 訪問端口 server.port=8080 # THYMELEAF (ThymeleafAutoConfiguration) # 開啟模板緩存(默認(rèn)值: true ) spring.thymeleaf.cache=false # 檢查模板是否存在,然后再呈現(xiàn) spring.thymeleaf.check-template=true # 檢查模板位置是否正確(默認(rèn)值 :true ) spring.thymeleaf.check-template-location=true #Content-Type 的值(默認(rèn)值: text/html ) spring.thymeleaf.content-type=text/html # 開啟 MVC Thymeleaf 視圖解析(默認(rèn)值: true ) spring.thymeleaf.enabled=true # 模板編碼 spring.thymeleaf.encoding=UTF-8 # 要被排除在解析之外的視圖名稱列表,?逗號分隔 spring.thymeleaf.excluded-view-names= # 要運?于模板之上的模板模式。另? StandardTemplate-ModeHandlers( 默認(rèn)值: HTML5) spring.thymeleaf.mode=HTML # 在構(gòu)建 URL 時添加到視圖名稱前的前綴(默認(rèn)值: classpath:/templates/ ) spring.thymeleaf.prefix=classpath:/templates/ # 在構(gòu)建 URL 時添加到視圖名稱后的后綴(默認(rèn)值: .html ) spring.thymeleaf.suffix=.html #上傳的絕對路徑 file.upload.path=d://save/images/ #絕對路徑下的相對路徑 file.upload.relativePath=/images/ #文件上傳大小限制 spring.servlet.multipart.max-file-size=2048000
實體類
注意:日期和圖片處理
@Data
@Entity(name = "book") //要求必須有@Id 也
@ApiModel(value = "圖書實體", description = "圖書中明細(xì)屬性")
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) //注意:默認(rèn)的是序列,針對Oracle的
private Integer bookId;
@ApiModelProperty(value = "圖書名")
private String bookName;
@ApiModelProperty(value = "圖書作者")
private String author;
private Float price;
@Column(name = "pic")
private String picpath; // 封面
@DateTimeFormat(pattern = "YYYY-MM-dd")
private Date pubDate; //出版日期
}DAO
dao 接口直接使用 springdata 提供的統(tǒng)一接口 JpaRepository ,其中已經(jīng)包含了基本的操作。也可以按約定規(guī)則自己定義其他方法
注意:繼承接口時需要指定泛型
public interface IBookDao extends JpaRepository<Book, Integer> {
List<Book> queryBooksByBookName(String bookName);
List<Book> findBooksByPriceBetween(Float min, Float max);
List<Book> findBooksByBookNameLike(String bookName);
List<Book> findAllByPriceOrderByPrice(float price);
@Query(
"select bookId,bookName,price,author from Book where bookName like :bookname"
)
Object[] queryBook(@Param("bookname") String bookName);
//HQL 語句 select book from Book book where ...
@Query("from Book where bookName like :bookname")
List<Book> queryBooks(@Param("bookname") String bookName);
}Service
接口和實現(xiàn)類
public interface IBookService {
void add(Book book);
void delete(Integer bookId);
Book detail(Integer bookId);
List<Book> queryAll();
void update(Book book);
}package com.wdzl.service.impl;
import com.wdzl.dao.IBookDao;
import com.wdzl.pojo.Book;
import com.wdzl.service.IBookService;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @Author: zhang
* @Date:2022/8/12
* @Description:
*/
@Service
public class BookService implements IBookService {
@Autowired
private IBookDao bookDao;
@Override
public void add(Book book) {
System.out.println(book.getBookId());
bookDao.save(book); // 注意: 如果對象在數(shù)據(jù)庫中存在的,執(zhí)行修改。
System.out.println(book.getBookId());
}
@Override
public void delete(Integer bookId) {
bookDao.deleteById(bookId);
}
@Override
public Book detail(Integer bookId) {
return bookDao.findById(bookId).get();
}
@Override
public List<Book> queryAll() {
return bookDao.findAll();
}
@Override
public void update(Book book) {
//如果對象是存在時,就是修改操作,如果不存在則插入操作
bookDao.save(book);
}
}到這里,就可以使用單元測試來測試 springdata 是否能正常使用了。
文件上傳
添加頁面
頁面必須
1.method必須是post
2.enctype="multipart/form-data" 必須
3.<input type="file"/>必須
static 目錄 下的 add.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Title</title>
</head>
<body>
<!--
文件上傳時:頁面必須
1.method必須是post
2.enctype="multipart/form-data" 必須
3.<input type="file"> 必須
-->
<h2>添加圖書</h2>
<form action="add" method="post" enctype="multipart/form-data">
<p>圖書名字:<input name="bookName" /></p>
<p>圖書價格:<input name="price" /></p>
<p>圖書作者:<input name="author" /></p>
<p>出版日期:<input name="pubDate" type="date" /></p>
<p>圖書封面:<input name="pic" type="file" /></p>
<p><input type="submit" value="保存" /></p>
</form>
</body>
</html>控制器
注意文件上傳處理:單獨上傳、文件名重命名、保存路徑的配置等
package com.wdzl.controller;
import com.wdzl.pojo.Book;
import com.wdzl.service.IBookService;
import com.wdzl.util.FileUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;
/**
* @Author: zhang
* @Date:2022/8/12
* @Description:
*
*/
@Controller
public class BookController {
@Autowired
private IBookService bookService;
@Value("${file.upload.savepath}")
private String savePath; //保存文件根目錄
@Value("${file.upload.relativePath}")
private String relativePath;
/**
* 日期處理
* 文件上傳
* @param book
* @return
*/
@PostMapping("add")
public String add(Book book, MultipartFile pic) {
System.out.println("============add()========");
String oriName = pic.getOriginalFilename(); //原始文件命名
//判斷目錄是否存在并創(chuàng)建
File rootDir = new File(savePath);
if (rootDir.exists() == false) {
rootDir.mkdirs();
}
if (!pic.isEmpty()) {
//文件名
String fileName = FileUtils.rename(oriName);
File saveFile = new File(rootDir, fileName);
//轉(zhuǎn)存到指定文件中
try {
pic.transferTo(saveFile);
System.out.println(">>>>>>文件保存在:" + saveFile.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
// 文件相對路徑 用來入庫和回顯
fileName = relativePath + fileName;
book.setPicpath(fileName);
}
//入庫
bookService.add(book);
return "redirect:list"; // /list
}
@GetMapping("list")
public String list(ModelMap modelMap) {
List<Book> list = bookService.queryAll();
modelMap.put("booklist", list);
return "list"; ///templates/list.html
}
@GetMapping("detail")
public String detail(Integer bookId, ModelMap modelMap) {
Book book = bookService.detail(bookId);
modelMap.put("book", book);
return "detail";
}
}列表頁面
添加成功后,跳轉(zhuǎn)到列表頁面顯示,下面使用的 thymeleaf 遍歷顯示
注意:下面圖片路徑、鏈接等處理
templates 下的 list.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>圖書列表</title>
<style type="text/css" rel="stylesheet">
div {
margin: 30px;
text-align: center;
}
</style>
</head>
<body>
<a href="add.html">添加圖書</a>
<hr />
<div>
<table width="75%">
<thead>
<tr bgcolor="#556b2f">
<th>序號</th>
<th>書名</th>
<th>作者</th>
<th>價格</th>
<th>出版日期</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr th:each="book,st:${booklist}" th:bgcolor="${st.even?'#aac':''}">
<td th:text="${st.count}"></td>
<td>
<a
th:href="${'detail?bookId='+book.bookId}"
th:text="${book.bookName}"
></a>
</td>
<td th:text="${book.author}"></td>
<td th:text="${book.price}"></td>
<td th:text="${#dates.format(book.pubDate,'yyyy年MM月dd日')}"></td>
<td>
<a th:href="${'del?bookId='+book.bookId}">刪除</a>
</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>運行測試問題
到此,可以通過前端的 add.html 來實現(xiàn)添加和上傳操作了。
注意:默認(rèn)文件上傳大小是 1M,大于 1M 的會 500 異常。
可以通過配置修改默認(rèn)文件大小限制:
#文件上傳大小限制 spring.servlet.multipart.max-file-size=2048000
全局異常處理
在項目發(fā)布運行中,不希望直接顯示 500 異常頁面時,可以配置全局異常解析器來進(jìn)行處理
異常處理在 springboot 中有多種方式,下面介紹兩種
1. @ControllerAdvice + @ExceptionHandler
位置:在啟動類同包及子包下定義類
@ControllerAdvice
public class GlobalExceptionHander {
@ExceptionHandler(Exception.class)
public String doException(Exception ex) { // 不能使用model 無法傳參到頁面顯示
System.out.println(">>>>==異常了:" + ex.toString());
return "error"; // 轉(zhuǎn)發(fā)到 error.html 頁面
}
}
上面的 @ControllerAdvice 注解中已經(jīng)包含 @Component 注解,所以直接會被 spring 掃描加入容器中。
2. @Configuration+SimpleMappingExceptionResolver
/**
* @Author: zhang
*/
@Configuration
public class ApplicationConfig {
/**
* 全局異常配置
* 頁面可以通過 exception對象來獲取
*/
@Bean
public SimpleMappingExceptionResolver doException() {
SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
Properties properties = new Properties();
properties.setProperty("java.lang.Exception", "error"); //映射異常類型和轉(zhuǎn)發(fā)的頁面對應(yīng)關(guān)系
resolver.setExceptionMappings(properties);
System.out.println("===========異常配置======");
return resolver;
}
}上面是一個 @Configuration 標(biāo)注的配置類。異常對象會被轉(zhuǎn)發(fā)到頁面。
圖片回顯
在完成上面的文件上傳和圖書信息添加之后,跳轉(zhuǎn)到圖書列表頁面,可以通過圖書名鏈接打開圖書詳細(xì)信息。
但在顯示圖片靜態(tài)資源時,路徑問題導(dǎo)致圖片無法正常顯示。
下面來再來處理下文件上傳和回顯下載或顯示問題
1. 回顧保存方式
先來回顧上傳時對于圖片保存路徑的設(shè)置
首先我們先在配置文件中自定義了兩個路徑:絕對路徑和相對路徑
#上傳的絕對路徑, 文件保存的真正的目錄位置 file.upload.path=d://save/images/ #絕對路徑下的相對路徑 用于前端請求調(diào)用的邏輯地址,默認(rèn)是不能直接使用的 file.upload.relativePath=/images/
在控制器保存圖片時,使用上面地址保存圖片和入庫記錄
@PostMapping("add") //注意這里 pic 需要單獨和頁面元素對應(yīng),實體類中只是不同名的字符串用來存地址
public String add(Book book, @ApiParam(value = "圖片文件", required = true)MultipartFile pic){
System.out.println(book+"===="+pic);
String fileName = pic.getOriginalFilename();
//重命名
fileName = FileUtils.rename(fileName);
// 保存到磁盤
File saveDir = new File(savePath); //====================這里是真實保存目錄
if(saveDir.exists()==false){
saveDir.mkdirs();//創(chuàng)建保存圖片的目錄
}
File saveFile = new File(saveDir,fileName);
try {
pic.transferTo(saveFile);
System.out.println("圖片保存在:"+saveFile.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
//保存的相對路徑
String picPath = relativePath + fileName; //==========這里邏輯目錄 虛擬的
System.out.println(">>>入庫路徑:"+picPath);
book.setPicpath(picPath); //保存到實體類 入庫
//入庫
bookService.add(book);
return "redirect:list";
}首先從上面代碼中可以看到,保存的磁盤的目錄和入庫的路徑是不同的,默認(rèn)是不對應(yīng)不能訪問的。
頁面中使用的路徑為數(shù)據(jù)庫中的相對邏輯路徑
<img th:src="${book.picpath}" width="200" /><!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>圖書明細(xì)</title>
</head>
<body>
<div>
<p>
<img th:src="${book.picpath}" width="200" />
</p>
<p>書名:<span th:text="${book.bookName}"></span></p>
<p>作者:<span th:text="${book.author}"></span></p>
<p>價格:<span th:text="${book.price}"></span></p>
<p>
出版日期:<span
th:text="${#dates.format(book.pubDate,'yyyy-MM-dd')}"
></span>
</p>
</div>
</body>
</html>2. 配置資源映射
如果需要能正常的訪問,則使用下面的配置進(jìn)行映射
實現(xiàn) WebMvcConfigurer 同時 標(biāo)注 @Configuration
/**
* @Author: zhang
* @Date:2022/8/11
* @Description:
*/
@Configuration
public class ApplicationConfig implements WebMvcConfigurer {
@Value("${file.upload.path}")
private String savePath;
@Value("${file.upload.relativePath}")
private String relativePath;
/**
* 資源路徑映射
* 注意:路徑前加 "file:/"
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
System.out.println(relativePath + "==============" + savePath);
registry
.addResourceHandler(relativePath + "/**")
.addResourceLocations("file:/" + savePath);
}
}通過上面的配置后,再去訪問就可以正常顯示圖片了。
以上就是基于SpringBoot實現(xiàn)圖片上傳及圖片回顯的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot圖片上傳 回顯的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot整合Thymeleaf與FreeMarker視圖層技術(shù)
在目前的企業(yè)級應(yīng)用開發(fā)中,前后端分離是趨勢,但是視圖層技術(shù)還占有一席之地。Spring Boot 對視圖層技術(shù)提供了很好的支持,福安防推薦使用的模板引擎是Thymeleaf,不過想FreeMarker也支持,JSP技術(shù)在這里并不推薦使用2022-08-08
SpringCloud超詳細(xì)講解Feign聲明式服務(wù)調(diào)用
Feign可以把Rest的請求進(jìn)行隱藏,偽裝成類似Spring?MVC的Controller一樣。不用再自己拼接url,拼接參數(shù)等等操作,一切都交給Feign去做2022-06-06
idea 創(chuàng)建properties配置文件的步驟
這篇文章主要介紹了idea 創(chuàng)建properties配置文件的步驟,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-01-01

