SpringBoot で RestAPI で画像の受け渡し時のメモ
画面側から fetch で post で body につめて送る感じでやってみる
受けた後の処理はご自由にで一旦は json でもらったものをそのまま返すだけにしてます
実行環境
- Windows11
- Pleiades 2022 Full Edition
- Maven
- SpringFramework Boot v3.1.5
- Java v17
- spring-boot-starter-thymeleaf
- spring-boot-starter-web
プロジェクト依存関係
雑に pom.xml の中身です
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>springboot-maven2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-maven2</name>
<description>Demo project for Spring Boot 3.0.0</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<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>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
コントローラ作成
コントローラとして2つ作成する
- 画面表示用
HomeController
- 画像RestAPI
TakeController
画面表示用 HomeController
HomeController.java
package samp.cont;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class HomeController {
@GetMapping("/home")
public String home() {
return "home";
}
}
画像RestAPI TakeController
post の受け取り方は DTO みたいなクラス作るパターンと Map で受ける2パターン作ってます
TakeController.java
package samp.cont;
import java.util.Map;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TakeController {
// こっちは DTO で受けるパターン
@PostMapping("/take1")
public Take take1(@RequestBody Take take) {
return take;
}
// こっちは Map で受けるパターン
@PostMapping("/take2")
public Map<String, String> take2(@RequestBody Map<String, String> take) {
return take;
}
}
こちらは DTO として作った Take クラス
Take.java
package samp.cont;
import lombok.Data;
@Data
public class Take {
private String url;
}
JavaScript 作成
デバイスのカメラ設定とhtmlのDOMへの関連付けを initVideoCamera で処理してます
撮影して ReasAPI 実行のところを photoShoot で処理してます
script.js
const video = document.querySelector('#video');
const canvas = document.createElement('canvas');
initVideoCamera();
document.querySelector('#shoot1').addEventListener('click', () => photoShoot('take1'));
document.querySelector('#shoot2').addEventListener('click', () => photoShoot('take22'));
// ビデオのカメラ設定(デバイスのカメラ映像をビデオに表示)
function initVideoCamera() {
navigator.mediaDevices.getUserMedia({ video: true, audio: false })
.then((stream) => {
video.srcObject = stream;
video.play();
})
.catch(e => console.log(e));
}
// カメラ撮影して RestAPI 実行
function photoShoot(take) {
let drawSize = calcDrawSize();
canvas.width = drawSize.width;
canvas.height = drawSize.height;
const context = canvas.getContext("2d");
context.drawImage(video, 0, 0, canvas.width, canvas.height);
fetch(`/${take}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({url: canvas.toDataURL("image/png")}),
}).then(res => res.json()).then(json=>{
document.querySelector("#photo").src = json.url;
});
}
// 描画サイズの計算
// 縦横比が撮影(video)が大きい時は撮影の縦基準、それ以外は撮影の横基準で計算
function calcDrawSize() {
let videoRatio = video.videoHeight / video.videoWidth;
let viewRatio = video.clientHeight / video.clientWidth;
return videoRatio > viewRatio ?
{ height: video.clientHeight, width: video.clientHeight / videoRatio }
: { height: video.clientWidth * videoRatio, width: video.clientWidth }
}
HTML 作成
こちらは細かいこと気にしない
カメラの映像表示する枠と撮影した後の画像表示する枠を準備
home.html
<!DOCTYPE html>
<html>
<head>
<title>Take Sample</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" th:href="@{style.css}" >
</head>
<body>
<div class="flex-row">
<div class="camera">
<video id="video"></video>
<div id="shoot1" class="shoot">撮影1</div>
<div id="shoot2" class="shoot">撮影2</div>
</div>
<img id="photo">
</div>
</body>
<script th:src="script.js"></script>
</html>
CSS 作成
こちらも細かいこと気にしない
適当にととのえる感じです
style.css
@charset "utf-8";
body {
text-align: center;
}
.flex-row {
display: flex;
justify-content: center;
}
#video {
border: 1px solid black;
width: 320px;
height: 240px;
}
#photo {
border: 1px solid black;
width: 320px;
height: 240px;
}
.shoot {
width: 100px;
margin: 5px auto;
border: solid 1px skyblue;
border-radius: 30px;
background-color: skyblue;
}
.shoot:hover {
opacity: 0.7;
}
p.ret-element {
margin-top: 0;
}
@media (width <=1000px) {
.flex-row {
display: block;
}
}
実行して起動して確認
撮影おして写真が描画されればOK
RestAPI のコントローラ側でなんか処理して保存とかしても良いかも
実行画面はこんな感じ...