0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Spring Boot】Cloudinaryを使って画像をクラウド保存する方法(完全ガイド)

Posted at

概要

Webアプリケーション開発において、ユーザーのプロフィール画像や商品画像の保存先は悩みの種になりがちです。 ローカルサーバーに保存すると、HerokuやRenderなどのPaaSデプロイ時にデータが消えてしまったり、スケーリングが難しくなったりします。
そこで今回は、画像管理クラウドサービスCloudinaryとSpring Bootを連携させ、画像をアップロードしてURLを取得・保存する一連の流れを解説します。

この記事で実現できること

  • Spring Bootから画像をアップロードする
  • Cloudinaryに画像を保存し、公開URL(secure_url)を取得する
  • 取得したURLをDBのユーザー情報として保存する

1. 依存関係の追加 (pom.xml)

<dependencies>
    <dependency>
        <groupId>com.cloudinary</groupId>
        <artifactId>cloudinary-http44</artifactId>
        <version>1.39.0</version>
    </dependency>
</dependencies>

2. APIキーの設定

Cloudinaryのダッシュボードから Cloud Name, API Key, API Secret を取得し、設定ファイルに記述します。

注意: セキュリティのため、APIキーやシークレットはGitにコミットせず、環境変数として管理することを推奨します。

#application.yml

# Cloudinary config
cloudinary:
  cloud_name: ${CLOUDINARY_CLOUD_NAME}
  api_key: ${CLOUDINARY_API_KEY}
  api_secret: ${CLOUDINARY_API_SECRET}

もしくは

#application.properties

# Cloudinary config
cloudinary.cloud_name=${CLOUDINARY_CLOUD_NAME}
cloudinary.api_key=${CLOUDINARY_API_KEY}
cloudinary.api_secret=${CLOUDINARY_API_SECRET}

CloudinaryConfig作成

CloudinaryConfig.java Springの @Configuration クラスを作成し、CloudinaryインスタンスをBeanとして登録します。これでどこからでもDI(依存性注入)が可能になります。

import com.cloudinary.Cloudinary;
import com.cloudinary.utils.ObjectUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class CloudinaryConfig {

    @Value("${cloudinary.cloud_name}")
    private String cloudName;

    @Value("${cloudinary.api_key}")
    private String apiKey;

    @Value("${cloudinary.api_secret}")
    private String apiSecret;

    @Bean
    public Cloudinary cloudinary() {
        return new Cloudinary(ObjectUtils.asMap(
                "cloud_name", cloudName,
                "api_key", apiKey,
                "api_secret", apiSecret));
    }
}

3.アップロード処理の実装(サービス)

Cloudinaryへのアップロード処理を担当するサービスクラスです。 MultipartFile(フォームから送られたファイルデータ)を受け取り、Cloudinaryへ送信します。

import com.cloudinary.Cloudinary;
import com.cloudinary.utils.ObjectUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.Map;

@Service
public class CloudinaryService {
    private final Cloudinary cloudinary;

    public CloudinaryService(Cloudinary cloudinary) {
        this.cloudinary = cloudinary;
    }

    public String uploadFile(MultipartFile file, String folder) throws IOException {
        // Cloudinaryへアップロード
        // folderオプションを指定することで、Cloudinary内のフォルダ分けが可能
        Map uploadResult = cloudinary.uploader().upload(
                file.getBytes(),
                ObjectUtils.asMap("folder", folder) 
        );

        // アップロード後のURL(https://...)を取得して返す
        return uploadResult.get("secure_url").toString();
    }
}

4.コントローラー実装

実際にAPIとして叩くエンドポイントを作成します。 ここでは「ユーザーIDを指定してプロフィール画像を更新する」というシナリオです。

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.UUID;

@RestController
@RequestMapping("/api/v1/users")
public class UserController {

    private final CloudinaryService cloudinaryService;
    private final UserService userService;

    public UserController(CloudinaryService cloudinaryService, UserService userService) {
        this.cloudinaryService = cloudinaryService;
        this.userService = userService;
    }

    @PutMapping("/{id}/profile-image")
    public ResponseEntity<ApiResponse<String>> uploadProfileImage(
            @PathVariable UUID id,
            @RequestParam("file") MultipartFile file) {
        
        ApiResponse<String> res = new ApiResponse<>();
        
        try {
            // 1. 画像をCloudinaryにアップロードし、URLを取得
            // "profile_images" というフォルダに保存されます
            String url = cloudinaryService.uploadFile(file, "profile_images");

            // 2. ユーザーをDBから取得
            User user = userService.getUserById(id)
                    .orElseThrow(() -> new RuntimeException("ユーザーが見つかりません"));

            // 3. DBに画像のURLのみを保存(バイナリデータは保存しない)
            user.setProfileImageUrl(url);
            userService.save(user);

            res.setStatus("success");
            res.setMessage("プロフィール画像を更新しました");
            res.setData(url);
            
            return ResponseEntity.ok(res);

        } catch (Exception e) {
            res.setStatus("error");
            res.setMessage("アップロードに失敗しました: " + e.getMessage());
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(res);
        }
    }
}

### 補足:ファイルサイズ制限の解除
Spring Bootのデフォルトでは、アップロードできるファイルサイズが 1MB に制限されています。これを超える画像をアップロードしようとすると MaxUploadSizeExceededException が発生します。

application.yml に以下を追加して上限を緩和しておきましょう。

spring:
  servlet:
    multipart:
      max-file-size: 10MB
      max-request-size: 10MB

まとめ

日本語情報の少ない「Spring Boot × Cloudinary」の連携ですが、ストレージ制限のあるHerokuやRender等のPaaS環境では非常に有効な手段です。 画像のリサイズ・圧縮も手軽に実装できるため、個人開発の画像管理に悩む方はぜひ試してみてください。

0
0
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
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?