ContentVersionでファイルをレコードへ添付する実装
apex:inputFileで受け取ったファイルをContentVersionとして保存し、ContentDocumentLinkで取引先に紐づけます。ファイルサイズとApexのヒープ上限の関係も説明します。
ファイルの保存先はContentVersion
Visualforceページから受け取ったファイルをレコードに添付する実装です。保存先にはAttachmentではなくContentVersionを使います。AttachmentはまだAPIに残っていますが、現在の実装ではContentVersionとContentDocumentLinkを使うのが基本です。ファイルの共有範囲を細かく制御でき、Salesforce Filesの機能ともそのままつながります。
ファイルを受け取るVisualforceページ
apex:inputFileでファイル選択欄を作ります。valueにファイルの中身、fileNameにファイル名を受け取るプロパティを指定します。
<apex:page controller="FileAttachmentController"> <apex:form id="form"> <div> <apex:inputFile value="{!attachFile.VersionData}" fileName="{!attachFile.PathOnClient}" id="attachFile"/> </div> <div> <apex:commandButton action="{!saveAttachFile}" value="保存" id="saveAttachFile"/> </div> </apex:form> </apex:page>
ファイル選択欄と保存ボタンだけを置いたページです。ファイルを選ばずに保存を押しても取引先だけは作成されます。
apex:inputFileはAttachmentだけでなく、Blob型のプロパティであればどのsObjectのフィールドにもバインドできます。ここではContentVersionのVersionData(ファイルの中身)とPathOnClient(ファイル名)に直接バインドしています。
Apexコントローラの実装
コンストラクタでContentVersionを初期化し、保存処理では取引先を作成してからファイルをContentVersionとして登録し、ContentDocumentLinkで紐づけます。
public with sharing class FileAttachmentController { public ContentVersion attachFile { get; set; } public FileAttachmentController() { this.attachFile = new ContentVersion(); } public PageReference saveAttachFile() { Savepoint sp = Database.setSavepoint(); try { Account acc = new Account(Name = 'test'); insert acc; if (this.attachFile.VersionData != null) { this.attachFile.Title = this.attachFile.PathOnClient; insert this.attachFile; ContentDocumentLink cdl = new ContentDocumentLink(); cdl.ContentDocumentId = [ SELECT ContentDocumentId FROM ContentVersion WHERE Id = :this.attachFile.Id ].ContentDocumentId; cdl.LinkedEntityId = acc.Id; cdl.ShareType = 'V'; insert cdl; } } catch (Exception e) { Database.rollback(sp); } return null; } }
ファイルをContentVersionとして保存し、新規作成した取引先へContentDocumentLinkで紐づけます。保存に失敗した場合は取引先の作成もロールバックします。
ContentVersionはTitleが必須項目です。apex:inputFileのfileNameで受け取ったPathOnClientをそのままTitleに設定してからinsertします。ContentDocumentIdはinsertした直後にSOQLで取得します。ContentDocumentLinkのShareTypeを'V'にすると、リンク先に閲覧者(Viewer)の権限が付きます。
Attachmentを使ったコードは、ParentIdにレコードIdを入れるだけで済みました。ContentVersionでは「ファイルを保存する処理」と「レコードに紐づける処理」の2段階に分かれます。片方だけ失敗すると、ファイルだけが誰にも紐づかずに残ることがあります。ファイルサイズとヒープの上限
apex:inputFileでアップロードできるファイルサイズは、最大10MBです。この上限を超えるファイルは選択してもアップロードされません。
10MB以内であっても油断はできません。Apexの同期処理におけるヒープサイズの上限は6MBです。VersionDataとして受け取ったファイルの中身はApexのヒープに乗るため、6MBを超えるファイルを同期的に処理するとSystem.LimitExceptionになります。バッチApexや@futureメソッドなど非同期処理のヒープ上限は12MBですが、この記事のようにボタンから直接呼び出す同期処理では6MBが上限です。
Visualforceのページ自体にも上限があります。ページの状態を保持するビューステートは170KBまでです。フォームの項目やコントローラの変数が増えるページほど、この上限に近づきます。
使用例
ページを開く
ファイル選択欄と「保存」ボタンだけが表示されます。まだファイルは選ばれていません。
ファイルを選択する
「ファイルを選択」ボタンを押してファイルを選ぶと、ブラウザの標準機能で選んだファイル名が入力欄の横に表示されます。
保存を押す
「保存」ボタンを押すと、新しい取引先が作成され、選んだファイルがContentVersionとして保存されます。対象の取引先を開き、関連リストの「ファイル」を確認すると、添付したファイルが表示されます。
⚠️ 取引先の「ファイル」関連リストが表示されない場合は、ページレイアウトに関連リストが追加されているかを確認してください。ContentDocumentLinkで紐づけても、関連リスト自体がレイアウトになければ画面には表示されません。
ここで間違えやすい
| 間違い | 何が起きるか |
|---|---|
Attachmentのままレコードに保存する | 現在の実装では、ファイルの保存にはContentVersionとContentDocumentLinkを使います |
ContentVersionのTitleを設定し忘れる | Titleは必須項目のため、insertが例外になります |
| 10MBを超えるファイルを選ぶ | apex:inputFileの上限に達し、アップロードできません |
| 保存ボタンを連打する | クリックのたびに取引先とContentVersionが作られ、重複登録になります |
Attachment時代の感覚でBodyやParentIdを参照する | ContentVersionにはその項目がありません。VersionDataとContentDocumentLinkに置き換えます |
確認した環境
- 2026年9月 / Salesforce Summer '26(APIバージョン67.0)時点の公式ドキュメントで、apex:inputFileのファイルサイズ上限、ContentVersion・ContentDocumentLinkの仕様、Apexのヒープサイズ上限、Visualforceのビューステート上限を確認しています
まとめ
- ファイル添付の保存には、AttachmentではなくContentVersionとContentDocumentLinkを使います
apex:inputFileでアップロードできるファイルサイズは10MBまでです- Apexの同期処理のヒープ上限は6MBです。大きいファイルを扱うとここに当たります
- Visualforceのビューステートには170KBの上限があります。項目や変数が多いページほど近づきます
ContentVersionのTitleは必須項目です。保存前に設定します
参考:当時の画面
記事を最初に書いた当時の画面です。いまの手順と違うところは、各画像の説明に書いています。
Salesforceの導入・運用についてご相談ください
導入前の検討から、お使いの環境の改修・運用、AIとの連携まで承ります。状況を伺ったうえで、進め方をご提案します。