概要
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環境では非常に有効な手段です。 画像のリサイズ・圧縮も手軽に実装できるため、個人開発の画像管理に悩む方はぜひ試してみてください。