昨天的文章推送中有一篇題爲全網最通俗易懂的【短連接】入門, 讓我以爲頗爲有趣好玩,這不正好理論知識學完了,實操代碼擼起來。若是有不瞭解的同窗能夠看看入門那篇的介紹,我這裏直接從實戰提及,代碼中有超過的中文註釋,讓你更容易閱讀理解。話很少說,上代碼!html
新建一個普通的maven java 工程,如圖所示java
本身給項目取組名(group)和模塊名(artifact) 至此項目搭建完成,此時咱們爲項目引入一些基本的jar包<properties>
<spring.version>5.1.8.RELEASE</spring.version>
<spring.boot.version>2.1.6.RELEASE</spring.boot.version>
</properties>
<dependencies>
<!-- spring boot依賴,表示這是一個springboot web程序. -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
</dependency>
<!-- apache 的工具包. -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.7</version>
</dependency>
</dependencies>
複製代碼
添加必要的jar包web
<!-- 引入H2的相關包. -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
<!--輔助jar包,用於查看內存數據庫-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>${spring.boot.version}</version>
<optional>true</optional>
</dependency>
複製代碼
在application.properties中添加必要的配置算法
#h2配置
spring.jpa.show-sql = true
spring.jpa.hibernate.ddl-auto = update
##數據庫鏈接設置
spring.datasource.url = jdbc:h2:mem:dbtest
spring.datasource.username = root
spring.datasource.password = root
spring.datasource.driverClassName =org.h2.Driver
##數據初始化設置
#進行該配置後,每次啓動程序,程序都會運行resources/db/schema.sql文件,對數據庫的結構進行操做。
spring.datasource.schema=classpath:db/schema.sql
#進行該配置後,每次啓動程序,程序都會運行resources/db/data.sql文件,對數據庫的數據操做。
#spring.datasource.data=classpath:db/data.sql
##h2 web console設置
spring.datasource.platform=h2
# 進行該配置後,h2 web consloe 就能夠在遠程訪問了。不然只能在本機訪問。
spring.h2.console.settings.web-allow-others=true
#進行該配置,你就能夠經過YOUR_URL/h2訪問h2 web consloe。YOUR_URL是你程序的訪問URl。
spring.h2.console.path=/h2
#進行該配置,程序開啓時就會啓動h2 web consloe。固然這是默認的,若是你不想在啓動程序時啓動h2 web consloe,那麼就設置爲false。
spring.h2.console.enabled=true
複製代碼
添加數據庫結構腳本spring
在resource
目錄下新建文件夾db
,建立文件schema.sql
,內容以下sql
create table if not exists short_link (
id int not null primary key,
url varchar(1000),
create_time DATE );
複製代碼
測試H2數據庫數據庫
啓動springboot應用程序,在瀏覽器中輸入http://localhost:2088/h2
,能夠打開h2數據庫管理器登陸界面,可以進入以下頁面說明H2集成成功!apache
建立實體與數據庫表的映射對象瀏覽器
@Entity
@Data
@NoArgsConstructor
@ToString
public class ShortLink {
@Id
@GeneratedValue
private long id;
private String url;
private Date createTime;
public ShortLink(String url, Date date){
this.url = url;
this.createTime =date;
}
}
複製代碼
建立DAO數據庫交互層,CrudRepository
實現了對 DB 基本的增刪改查方法springboot
/**
* CrudRepository 實現了對 ShortLink 基本的增刪改查方法
* @author jiangpeng
* @date 2019/11/2715:29
*/
public interface ShortLinkRepository extends CrudRepository<ShortLink, Long> {
}
複製代碼
短連接的ID轉換生成器
/**
* 短連接生成
* 10進制、62進制互轉
* @author jiangpeng
*/
@Slf4j
public class ConversionUtils {
/**
* 初始化 62 進制數據,索引位置表明字符的數值,好比 A表明10,z表明61等
*/
private static String chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
private static int scale = 62;
/**
* 將數字轉爲62進制
*
* @param num Long 型數字
* @param length 轉換後的字符串長度,不足則左側補0
* @return 62進制字符串
*/
public static String encode(long num, int length) {
StringBuilder sb = new StringBuilder();
int remainder;
// id混淆算法
long snum = num & 0xff000000;
snum += (num & 0x0000ff00) << 8;
snum += (num & 0x00ff0000) >> 8;
snum += (num & 0x0000000f) << 4;
snum += (num & 0x000000f0) >> 4;
while (snum > scale - 1) {
/*
對 scale 進行求餘,而後將餘數追加至 sb 中,因爲是從末位開始追加的,所以最後須要反轉(reverse)字符串
*/
remainder = Long.valueOf(snum % scale).intValue();
sb.append(chars.charAt(remainder));
snum = snum / scale;
}
sb.append(chars.charAt(Long.valueOf(snum).intValue()));
String value = sb.reverse().toString();
log.info("encode id: {}", snum);
return StringUtils.leftPad(value, length, '0');
}
/**
* 62進制字符串轉爲數字
*
* @param str 編碼後的62進制字符串
* @return 解碼後的 10 進制字符串
*/
public static long decode(String str) {
/*
將 0 開頭的字符串進行替換
*/
str = str.replace("^0*", "");
long num = 0;
int index;
for (int i = 0; i < str.length(); i++) {
/*
查找字符的索引位置
*/
index = chars.indexOf(str.charAt(i));
/*
索引位置表明字符的數值
*/
num += (long) (index * (Math.pow(scale, str.length() - i - 1)));
}
// id混淆算法
long snum = num & 0xff000000;
snum += (num & 0x00ff0000) >> 8;
snum += (num & 0x0000ff00) << 8;
snum += (num & 0x000000f0) >> 4;
snum += (num & 0x0000000f) << 4;
return snum;
}
/**
* @param args
*/
public static void main(String[] args) {
System.out.println("62進制:" + encode(1, 5));
System.out.println("10進制:" + decode("0000G"));
}
}
複製代碼
能夠執行main方法查看運行結果,比對是否進制轉後仍是原來的值
引入freeMarker的依賴包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
<version>${spring.boot.version}</version>
</dependency>
複製代碼
freeMarker默認讀取模板文件路徑爲resource/templates目錄下,因此在這個目錄下建立頁面文件 short_link.ftl
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8"/>
<title></title>
</head>
<body>
<h2>短連接轉換</h2>
<form action="shortLink" method="post">
要轉換的url:<textarea rows="1" style="width: 432px; height: 43px;" name="url">${url?default('')}</textarea>
<br/><br/>
<input type="submit" value="提交"/>
</form>
<#if shortUrl?? && shortUrl != "">
<a href="${shortUrl}" target="_blank">${shortUrl}</a>
</#if>
</body>
</html>
複製代碼
/**
* 生成短連接請求類
*
* @author jiangpeng
* @date 2019/11/2715:19
*/
@Controller
@RequestMapping("shortLink")
public class ShortLinkController {
@Autowired
private ShortLinkRepository shortLinkRepository;
@GetMapping
public String shortLink() {
return "short_link";
}
/**
* 生成短連接
*
* @param url 要轉換的url
* @return short_link.ftl
*/
@PostMapping
public String createShortLink(String url, HttpServletRequest request) throws UnknownHostException {
Instant instant = LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toInstant();
ShortLink shortLink = shortLinkRepository.save(new ShortLink(url, Date.from(instant)));
String shortStr = ConversionUtils.encode(shortLink.getId(), 4);
request.setAttribute("url", url);
request.setAttribute("shortUrl", getServerUrl(request) + "/shortLink/" + shortStr);
return "short_link";
}
/**
* 解析短連接並跳轉頁面
*
* @param shortUrl 短連接參數
*/
@RequestMapping("/{shortUrl}")
public void redirectToSourceUrl(@PathVariable("shortUrl") String shortUrl, HttpServletResponse response) throws IOException {
long id = ConversionUtils.decode(shortUrl);
Optional<ShortLink> shortLinkOpt = shortLinkRepository.findById(id);
String url = shortLinkOpt.orElseGet(null).getUrl();
response.sendRedirect(url);
}
/**
* 獲取當前應用服務器域名和端口
* @return String
*/
private String getServerUrl(HttpServletRequest request) throws UnknownHostException {
StringBuilder sb = new StringBuilder();
//獲取服務器域名
String serverName = request.getServerName();
//獲取服務器端口
int serverPort = request.getServerPort();
//獲取服務器IP地址;
String hostAddress = InetAddress.getByName(request.getServerName()).getHostAddress();
return sb.append("http://").append(serverName).append(":").append(serverPort).toString();
}
}
複製代碼
最後在瀏覽器輸入http://localhost:2088/shortLink
便可跳轉到對應頁面,以下圖是演示效果
引入zxing 二維碼工具包, 它實現了關於業界二維碼的規範
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.4.0</version>
</dependency>
複製代碼
/**
* 二維碼生成工具類
* @author jiangpeng
* @date 2019/11/28 0028
*/
@Slf4j
public class QRCodeUtils {
/**
* 生成二維碼
*
* @Param Content 二維碼內容
* @Param outputStream
*/
public static void QREncode(String content, File logoFile, OutputStream outputStream) throws WriterException,
IOException {
// 圖像寬度
int width = 200;
// 圖像高度
int height = 200;
// 圖像類型
String format = "png";
Map<EncodeHintType, Object> hints = new HashMap<>();
//內容編碼格式
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
// 指定糾錯等級
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
//設置二維碼邊的空度,非負數
hints.put(EncodeHintType.MARGIN, 1);
BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);
/*
問題:生成二維碼正常,生成帶logo的二維碼logo變成黑白; 緣由:MatrixToImageConfig默認黑白,須要設置BLACK、WHITE
解決:https://ququjioulai.iteye.com/blog/2254382
*/
if (logoFile != null) {
MatrixToImageConfig matrixToImageConfig = new MatrixToImageConfig(0xFF000001, 0xFFFFFFFF);
BufferedImage bufferedImage = LogoMatrix(MatrixToImageWriter.toBufferedImage(bitMatrix,
matrixToImageConfig),
logoFile);
//輸出帶logo圖片
ImageIO.write(bufferedImage, format, outputStream);
} else {
MatrixToImageWriter.writeToStream(bitMatrix, format, outputStream);
}
log.info("二維碼生成成功!");
}
/**
* 識別二維碼
*/
public static void QRReader(File file) throws IOException, NotFoundException {
MultiFormatReader formatReader = new MultiFormatReader();
//讀取指定的二維碼文件
BufferedImage bufferedImage = ImageIO.read(file);
BinaryBitmap binaryBitmap =
new BinaryBitmap(new HybridBinarizer(new BufferedImageLuminanceSource(bufferedImage)));
//定義二維碼參數
Map<DecodeHintType, String> hints = new HashMap<>(8);
hints.put(DecodeHintType.CHARACTER_SET, "utf-8");
Result result = formatReader.decode(binaryBitmap, hints);
//輸出相關的二維碼信息
log.info("解析結果:" + result.toString());
log.info("二維碼格式類型:" + result.getBarcodeFormat());
log.info("二維碼文本內容:" + result.getText());
bufferedImage.flush();
}
/**
* 二維碼添加logo
*
* @param matrixImage 源二維碼圖片
* @param logoFile logo圖片
* @return 返回帶有logo的二維碼圖片
*/
public static BufferedImage LogoMatrix(BufferedImage matrixImage, File logoFile) throws IOException {
/*
* 讀取二維碼圖片,並構建繪圖對象
*/
Graphics2D g2 = matrixImage.createGraphics();
int matrixWidth = matrixImage.getWidth();
int matrixHeight = matrixImage.getHeight();
/*
* 讀取Logo圖片
*/
BufferedImage logo = ImageIO.read(logoFile);
int logoWidth = matrixWidth / 4;
int logoHeight = matrixHeight / 4;
int x = matrixWidth / 10 * 4;
int y = matrixHeight / 10 * 4;
//開始繪製圖片
g2.drawImage(logo, x, y, logoWidth, logoHeight, null);
BasicStroke stroke = new BasicStroke(5, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
// 設置筆畫對象
g2.setStroke(stroke);
//指定弧度的圓角矩形
RoundRectangle2D.Float round = new RoundRectangle2D.Float(x, y, logoWidth, logoHeight, 20, 20);
g2.setColor(Color.white);
// 繪製圓弧矩形
g2.draw(round);
//設置logo 有一道灰色邊框
BasicStroke stroke2 = new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
// 設置筆畫對象
g2.setStroke(stroke2);
RoundRectangle2D.Float round2 = new RoundRectangle2D.Float(x + 2, y + 2, logoWidth - 4, logoHeight - 4, 20, 20);
g2.setColor(new Color(128, 128, 128));
// 繪製圓弧矩形
g2.draw(round2);
g2.dispose();
matrixImage.flush();
return matrixImage;
}
}
複製代碼
freeMarker默認讀取模板文件路徑爲resource/templates目錄下,因此在這個目錄下建立頁面文件 qr_code.ftl
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8"/>
<title></title>
</head>
<body>
<h2>二維碼內容生成</h2>
<form action="qrCode" method="post" enctype="multipart/form-data">
<span>內容:<input type="text" name="content"></span>
<br/><br/>
<span>logo:<input type="file" name="logoFile"></span>
<br/><br/>
<input type="submit" value="提交">
</form>
</body>
</html>
複製代碼
/**
* 生成二維碼請求類
* @author jiangpeng
* @date 2019/11/28 0028
*/
@Controller
@RequestMapping("qrCode")
public class QRCodeController {
/**
* 跳轉頁面
* @return
*/
@GetMapping
public String qrCode(){
return "qr_code";
}
/**
* 生成二維碼
* @param content 內容
* @param response HttpServletResponse
*/
@PostMapping
public void createQrCode(String content, @RequestParam("logoFile") MultipartFile logoFile, HttpServletResponse response) throws Exception {
File file = !logoFile.isEmpty() ? FileConvertUtils.multipartFileToFile(logoFile): null;
QRCodeUtils.QREncode(content, file, response.getOutputStream());
}
}
複製代碼
最後在瀏覽器輸入http://localhost:2088/shortLink便可跳轉到對應頁面,以下圖是演示效果
寫做不易,若是文章對你有幫助,能否留下腳印留個贊~