0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

SpringBoot で RestAPI で写真とって画像の受け渡しやってみる

Last updated at Posted at 2024-02-07

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 のコントローラ側でなんか処理して保存とかしても良いかも
実行画面はこんな感じ...
image.png

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?