【128】Spring Boot 1 實現瀏覽器拍照上傳功能
阿新 • • 發佈:2018-12-19
最近寫了個DEMO,實現瀏覽器拍照並上傳圖片的功能。框架用了Spring Boot 1.5.17.RELEASE,Java版本是8 。我把程式碼傳到了碼雲上,專案地址:https://gitee.com/zhangchao19890805/csdnBlog
Git專案中的 blog128 資料夾就是這次的演示專案。整個專案使用 Maven 構建。前端使用了 Spring Boot 1 預設整合的 Thymeleaf 模板引擎。後端使用了RESTful 風格的 API。使用瀏覽器拍照功能的時候,推薦使用火狐瀏覽器。如果使用谷歌瀏覽器,需要注意新版本的谷歌瀏覽器可能需要 https 協議的網站域名。
下面說一下關鍵程式碼。
HtmlController.java 用於跳轉到 index.html
package zhangchao.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class HtmlController {
@RequestMapping(value="/" ,method=RequestMethod.GET)
public String index(Model model) {
model.addAttribute("userId", 2);
return "index";
}
}
index.html 前端頁面,位於src/main/resources/templates/ 資料夾下。
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>拍照上傳</title>
<link th:href="@{styles/index.css}" rel="stylesheet" type="text/css"/>
</head>
<body>
<div class="index__tips" id="index__tips"></div>
<div class="index__title">請將您的臉部正面放置於下方提示區域,並點選“拍照”按鈕。</div>
<div class="index__video-panel">
<!-- 顯示攝像頭拍攝到的視訊 -->
<video id="index__video" class="index__video"></video>
<!-- 視訊上的提示框 -->
<svg width="200" height="200" class="index__video-over">
<!-- 克萊因藍 #002FA7 -->
<polyline points="5,50 5,5 50,5" fill-opacity="0"
style="stroke:#002FA7;stroke-width:10"/>
<polyline points="5,150 5,195 50,195" fill-opacity="0"
style="stroke:#002FA7;stroke-width:10"/>
<polyline points="155,5 195,5 195,45" fill-opacity="0"
style="stroke:#002FA7;stroke-width:10"/>
<polyline points="155,195 195,195 195,155" fill-opacity="0"
style="stroke:#002FA7;stroke-width:10"/>
</svg>
<!-- 視訊上的拍照按鈕 -->
<div class="index__video-over-button" onclick="indexObj.uploadImg()">拍照並上傳</div>
</div>
<!-- 使用者ID -->
<input type="hidden" th:value="${userId}" id="index__user-id"/>
<!-- 用於給video標籤截圖的畫布 -->
<canvas id="index__canvas" style="display:none;"></canvas>
<script th:src="@{styles/axios.min.js}"></script>
<script th:src="@{styles/index.js}"></script>
</body>
</html>
index.css 位於src/main/resources/static/styles/ 資料夾下。
@CHARSET "UTF-8";
.index__tips{
width: 100%;
color: red;
font-size: 16px;
font-family: "microsoft yahei";
text-align: center;
}
.index__title{
font-size: 20px;
font-family: "microsoft yahei";
margin: 0 auto;
width: auto;
text-align: center;
padding: 10px 0 15px 0;
}
.index__video-panel{
width: 450px;
height: 300px;
padding:0;border:0;
z-index: 0;
margin: 0 auto;
}
.index__video{
width:450px;
height:300px;
position: relative;
z-index: 0;
/* float:left; */
}
.index__video-over{
/* float:left; */
position: relative;
z-index:1;
margin:-250px 0 0 125px;
}
.index__video-over-button{
position: relative;
z-index:1;
color: white;
background-color:#002FA7;
width: 100px;
height: 30px;
line-height: 30px;
margin:-40px 0 0 175px;
text-align: center;
cursor: pointer;
}
index.js 位於src/main/resources/static/styles/ 資料夾下。
/**
* 拍照上傳圖片
*/
var indexObj = {};
indexObj.tips = "沒有檢測到裝置,請確保開啟攝像頭。";
// begin 顯示攝像頭的錄影。
navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia;
if (navigator.getUserMedia) {
navigator.getUserMedia({ audio: true, video: { width: 800, height: 450 } },
function(stream) {
var video = document.getElementById("index__video");
video.srcObject = stream;
console.log("stream active", stream)
video.onloadedmetadata = function(e) {
video.play();
};
},
function(err) {
console.log("The following error occurred: " + err.name);
document.getElementById("index__tips").innerHTML = indexObj.tips;
}
);
} else {
console.log("getUserMedia not supported");
document.getElementById("index__tips").innerHTML = indexObj.tips;
}
// end 顯示攝像頭的錄影。
/**
* 拍照上傳按鈕的事件響應
*/
indexObj.uploadImg = function(){
var canvas = document.getElementById("index__canvas");
var video = document.getElementById("index__video");
if (0 == video.videoWidth) {
document.getElementById("index__tips").innerHTML = indexObj.tips;
return;
}
// 讓canvas和視訊一樣寬高。
var w = video.videoWidth;
var h = video.videoHeight;
canvas.width = w;
canvas.height = h;
// 把video標籤中的畫面,畫到canvas中。
var ctx = canvas.getContext('2d');
ctx.drawImage(video, 0, 0, w, h);
// 把canvas中的影象轉換成png圖片檔案的Base64字串。
var imgStr = canvas.toDataURL('image/png').split("base64,")[1];
// 獲得使用者ID
var userId = document.getElementById("index__user-id").value;
axios.post("/api/profile/upload", {"userId":userId, "imgStr": imgStr})
.then(function(res){
console.log(res);
alert("上傳成功")
}).catch(function(error){
console.error(error);
})
}
ProfileController.java 處理上傳圖片。圖片用Base64傳輸。
package zhangchao.web;
import java.io.File;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import zhangchao.dto.ProfileUploadDTO;
import zhangchao.externalconfig.ZhangchaoSetting;
import zhangchao.sys.result.BaseResult;
import zhangchao.sys.result.ObjectOkResult;
import zhangchao.sys.utils.Base64Utils;
/**
* 作者
* @author jyn
*
*/
@RestController
@RequestMapping("/api/profile")
@Api(tags= {"profile"})
public class ProfileController {
@Autowired
private ZhangchaoSetting zhangchaoSetting;
@ApiOperation(value = "上傳拍照頭像。", notes = "上傳拍照頭像。用Base64傳輸圖片內容。", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@RequestMapping(value="/upload",method=RequestMethod.POST)
public BaseResult upload(@RequestBody ProfileUploadDTO profileUploadDTO){
ObjectOkResult r = new ObjectOkResult();
Integer userId = profileUploadDTO.userId;
String imgStr = profileUploadDTO.imgStr;
String basePath = this.zhangchaoSetting.getUploadPath();
String filePath = basePath + "/" + userId + ".png";
Base64Utils.createFile(imgStr, new File(filePath));
return r;
}
}
Base64Utils.java 處理Base64 的工具類。
package zhangchao.sys.utils;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import sun.misc.BASE64Encoder;
import sun.misc.BASE64Decoder;
public class Base64Utils {
/**
* 根據檔案獲取Base64字串
* @param file 檔案物件
* @return
*/
public static String getBase64Str(File file) {
String r = null;
FileInputStream fis = null;
byte[] data = null;
try {
fis = new FileInputStream(file);
data = new byte[fis.available()];
fis.read(data);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fis != null) {
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 防止data陣列為空。
if (null == data || data.length == 0) {
return null;
}
BASE64Encoder encoder = new BASE64Encoder();
r = encoder.encode(data);
return r;
}
/**
* 根據Base64字串建立檔案。
* @param base64Str Base64字串。
* @param file 要建立的檔案
* @return true表示建立成功。false表示建立失敗。
*/
public static boolean createFile(String base64Str, File file){
boolean flag = false;
if (null == base64Str) {
return flag;
}
BASE64Decoder decoder = new BASE64Decoder();
File dir = file.getParentFile();
FileOutputStream out = null;
try {
if (!dir.exists()) {
dir.mkdirs();
}
if (!file.exists()) {
file.createNewFile();
}
byte[] b = decoder.decodeBuffer(base64Str);
for(int i=0;i<b.length;++i)
{
if(b[i]<0)
{//調整異常資料
b[i]+=256;
}
}
//生成檔案
out = new FileOutputStream(file);
out.write(b);
flag = true;
} catch (IOException e) {
e.printStackTrace();
flag = false;
} finally {
try {
if (null != out) {
out.flush();
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return flag;
}
}
這個專案上傳圖片存放的硬碟路徑寫在配置檔案中。檔案是application.properties 。
配置項是
zhangchao.uploadPath=E:/test
讀取配置項的Java程式碼是 ZhangchaoSetting.java 。 程式碼如下:
package zhangchao.externalconfig;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix="zhangchao")
public class ZhangchaoSetting {
private String uploadPath;
public String getUploadPath() {
return uploadPath;
}
public void setUploadPath(String uploadPath) {
this.uploadPath = uploadPath;
}
}
最後是系統執行後的效果圖: