ApexでAmazon S3のファイルを削除するときの注意
S3のファイルは消えたのか、もともと無かったのか、レスポンスだけでは見分けがつきません。DELETEリクエストの組み立て方と、複数件まとめて消すときにコールアウト回数へどう当たるかを説明します。
なぜ削除もコールアウトで完結させるのか
Salesforce側のレコードとS3側のファイルを紐づけて運用していると、レコードを削除したりファイルを差し替えたりするたびに、S3側にも不要になったファイルが残ります。コンソールから手作業で消すのは、件数が増えると追いつきません。Apexからのコールアウト1回で、対象のファイルだけを指定して消せます。
用意するもの
| 用意するもの | 内容 |
|---|---|
| S3バケット | 削除対象のファイルが置かれている先。本記事では example-bucket を使います |
| Salesforce側の認証経路 | S3への発信を許可し署名を作る仕組み |
| 削除対象を特定する情報 | Salesforce側のContentVersionに記録した、S3側のファイル名(キー) |
認証経路の考え方はアップロード・読み込みと同じです。アクセスキーとシークレットは、AWS Signature Version 4に対応した外部ログイン情報に保存し、Apexのコードへは書きません。以降のコード例は、DELETEリクエストの署名の仕組みを示すために自前でHMAC署名を組み立てていますが、実運用ではこの部分をSalesforceの外部ログイン情報に任せます。
削除対象のキーを特定する
S3上のファイル名(キー)を、アップロード時にContentVersionのどこかへ記録しておく必要があります。カスタム項目を1つ用意し、アップロードが成功した時点でキーを書き込んでおくと、削除のときにそのまま使えます。
SELECT Id, Title, S3_Object_Key__c FROM ContentVersion WHERE ContentDocumentId IN ( SELECT ContentDocumentId FROM ContentDocumentLink WHERE LinkedEntityId = :accountId ) ORDER BY CreatedDate DESC LIMIT 1
取引先に紐づく最新のファイルから、S3上のキーを保持したカスタム項目を取得します
以前の書き方ではAttachmentにキーを持たせていましたが、Attachmentは新規のファイル添付に使われなくなったため、ContentVersion側のカスタム項目で持ちます。
Apexから削除する
DELETEメソッドには送るボディがありません。署名文字列にも空のボディぶんが入ります。
public with sharing class S3FileDeleter { public static Integer deleteFromS3(String objectKey, String accessKey, String secretKey) { String bucketName = 'example-bucket'; String host = bucketName + '.s3.ap-northeast-1.amazonaws.com'; String encodedKey = EncodingUtil.urlEncode(objectKey, 'UTF-8'); String dateHeader = Datetime.now().formatGMT('EEE, dd MMM yyyy HH:mm:ss z'); String stringToSign = 'DELETE\n\n\n' + dateHeader + '\n' + '/' + bucketName + '/' + encodedKey; Blob mac = Crypto.generateMac('HMACSHA1', Blob.valueOf(stringToSign), Blob.valueOf(secretKey)); String authHeader = 'AWS' + ' ' + accessKey + ':' + EncodingUtil.base64Encode(mac); HttpRequest req = new HttpRequest(); req.setMethod('DELETE'); req.setEndpoint('https://' + host + '/' + encodedKey); req.setHeader('Host', host); req.setHeader('Date', dateHeader); req.setHeader('Authorization', authHeader); HttpResponse res = new Http().send(req); return res.getStatusCode(); } }
指定したキーのファイルをS3から削除します。対象が無くても例外にはならず、ステータスコードで結果を返します
削除結果の読み方
AWSの公式リファレンスによると、削除に成功すると204 No Contentが返ります。この204は、対象のキーがもともと存在しなかった場合にも同じく返ります。つまりS3の削除は、2回続けて同じキーを消そうとしても2回目もエラーにならない、べき等な作りになっています。「消えたこと」を確認したいときは、削除の前に一覧やヘッダー確認(HEAD)で存在を確かめておくか、削除後に別途一覧を取り直して確認します。ステータスコードが204だからといって、削除前にファイルが実在したとは限りません。
複数件まとめて消すときの注意
トリガーやフローの一括処理から、レコードごとに1回ずつ削除のコールアウトを送る形にすると、対象が増えたときに1トランザクションあたり最大100回というコールアウトの上限にすぐ当たります。トリガーの中からは同期のコールアウトを送れないため、削除処理はその場では行わず、キューアブルやバッチApexに渡して分割します。トリガーの中で直接コールアウトを送らないのは、削除に限らずコールアウト全般に共通する注意点です。
ここで間違えやすい
| 間違い | 何が起きるか |
|---|---|
| 204が返ったことを「削除できた」の確認にする | 対象が最初から無かった場合も204が返るため、実在の確認にはなりません |
| トリガーから1レコードずつ削除のコールアウトを直接送る | トリガーからは同期のコールアウトを送れず、実行時エラーになります。非同期処理に渡します |
Attachment側にS3のキーを持たせたままにする | Attachmentは新規のファイル添付に使われません。ContentVersion側へ移します |
| DELETEのボディに何か渡そうとする | S3のDELETEはボディを使いません。署名文字列側の扱いを間違えると403になります |
| アクセスキーとシークレットをApexの文字列に直書きする | 外部ログイン情報に置けば、コードには一切残りません |
確認した環境
- 2026年9月/Salesforce Summer '26(APIバージョン67.0)時点の公式ドキュメントで、コールアウトの制限と指定ログイン情報の仕様を確認しています
- S3のDeleteObjectが返すステータスコードの仕様は、AWSの公式リファレンスで確認しています
まとめ
- S3のファイル削除は、対象のキーを指定したDELETEリクエスト1回で完結します
- 削除成功は204で返りますが、対象が無かった場合も同じく204です。存在確認には使えません
- 複数件をまとめて消すときは、トリガーから直接コールアウトを送らず、キューアブルやバッチApexに分けます
- 削除対象のキーは
AttachmentではなくContentVersion側のカスタム項目で管理します - アクセスキーとシークレットは外部ログイン情報に置き、Apexのコードには書きません
参考:当時の画面
記事を最初に書いた当時の画面です。いまの手順と違うところは、各画像の説明に書いています。
Salesforceの導入・運用についてご相談ください
導入前の検討から、お使いの環境の改修・運用、AIとの連携まで承ります。状況を伺ったうえで、進め方をご提案します。