JavaFX+Spring Bootで社内向け画像管理ツールを作る方法【デスクトップ×API連携】

JavaでGUIとサーバー処理を分離した構成を実現したいと思ったことはありませんか?
この記事では、JavaFXによるデスクトップUIとSpring BootによるバックエンドAPIを組み合わせ、社内向けの画像管理ツールを作成する方法を紹介します。


対象読者

  • JavaでGUIアプリとREST APIの連携を学びたい方
  • 画像のアップロード・閲覧・削除などを行う簡易ツールを作りたい方
  • JavaFXとSpring Bootの実務レベル統合例を探している方

構成概要

以下のような構成で進めます

  • JavaFX: 画像選択、プレビュー、アップロード操作のUI
  • Spring Boot: 画像保存API(ローカルディスク保存)

1. Spring Bootで画像アップロードAPIを作成


// ImageController.java
@RestController
@RequestMapping("/api/images")
public class ImageController {

    private final Path storageDir = Paths.get("uploads");

    @PostConstruct
    public void init() throws IOException {
        Files.createDirectories(storageDir);
    }

    @PostMapping("/upload")
    public ResponseEntity<?> upload(@RequestParam("file") MultipartFile file) throws IOException {
        Path destination = storageDir.resolve(file.getOriginalFilename());
        Files.copy(file.getInputStream(), destination, StandardCopyOption.REPLACE_EXISTING);
        return ResponseEntity.ok("アップロード成功");
    }

    @GetMapping("/{filename}")
    public ResponseEntity<byte[]> download(@PathVariable String filename) throws IOException {
        Path filePath = storageDir.resolve(filename);
        byte[] content = Files.readAllBytes(filePath);
        return ResponseEntity.ok().body(content);
    }

    @DeleteMapping("/{filename}")
    public ResponseEntity<?> delete(@PathVariable String filename) throws IOException {
        Path filePath = storageDir.resolve(filename);
        Files.deleteIfExists(filePath);
        return ResponseEntity.ok("削除成功");
    }
}

依存ライブラリ(Maven)


<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>

2. JavaFXで画像アップロードUIを作成

JavaFXを使って、画像選択とプレビュー、API経由でのアップロードを行います。


public class ImageUploaderApp extends Application {

    private ImageView imageView;
    private File selectedFile;

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage stage) {
        Button selectButton = new Button("画像選択");
        Button uploadButton = new Button("アップロード");

        imageView = new ImageView();
        imageView.setFitWidth(300);
        imageView.setPreserveRatio(true);

        selectButton.setOnAction(e -> selectImage(stage));
        uploadButton.setOnAction(e -> uploadImage());

        VBox root = new VBox(10, selectButton, imageView, uploadButton);
        root.setPadding(new Insets(20));
        stage.setScene(new Scene(root));
        stage.setTitle("画像アップローダー");
        stage.show();
    }

    private void selectImage(Stage stage) {
        FileChooser chooser = new FileChooser();
        chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("画像ファイル", "*.jpg", "*.png"));
        selectedFile = chooser.showOpenDialog(stage);
        if (selectedFile != null) {
            imageView.setImage(new Image(selectedFile.toURI().toString()));
        }
    }

    private void uploadImage() {
        if (selectedFile == null) return;

        HttpClient client = HttpClient.newHttpClient();
        HttpRequest.BodyPublisher body = ofFileUpload("file", selectedFile);

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("http://localhost:8080/api/images/upload"))
                .header("Content-Type", "multipart/form-data; boundary=MyBoundary")
                .POST(body)
                .build();

        client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
              .thenAccept(response -> {
                  System.out.println("アップロード結果: " + response.body());
              });
    }

    // シンプルなmultipart/form-data作成
    private HttpRequest.BodyPublisher ofFileUpload(String name, File file) {
        String boundary = "MyBoundary";
        var byteArrays = new ArrayList<byte[]>();

        try {
            byteArrays.add(("--" + boundary + "\r\n").getBytes());
            byteArrays.add(("Content-Disposition: form-data; name=\"" + name + "\"; filename=\"" + file.getName() + "\"\r\n").getBytes());
            byteArrays.add(("Content-Type: application/octet-stream\r\n\r\n").getBytes());
            byteArrays.add(Files.readAllBytes(file.toPath()));
            byteArrays.add(("\r\n--" + boundary + "--\r\n").getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }

        return HttpRequest.BodyPublishers.ofByteArrays(byteArrays);
    }
}

3. 実行方法と構成

プロジェクト構成例:

📁 image-manager/
├─ 📁 backend/ (Spring Boot)
│   └─ ImageController.java
├─ 📁 frontend/ (JavaFX)
│   └─ ImageUploaderApp.java

1. Spring Bootアプリを localhost:8080 で起動
2. JavaFXアプリから画像を選択してアップロードボタンをクリック


まとめ

JavaFXとSpring Bootを組み合わせることで、「社内で使いやすいGUI+REST連携の業務ツール」が簡単に構築できます。
今回紹介した画像アップローダーのように、UIはデスクトップに残しつつ、データ処理をAPIに分離することで、保守性・拡張性の高いアーキテクチャを実現できます。

用途例:

  • 社内文書や証跡の画像管理
  • OCR前の画像収集
  • 社外提出資料のレビュー管理
タイトルとURLをコピーしました