Apex

Visualforceでテキストファイルを出力する2つの方法

取引先の詳細画面のボタンから、取引先名をテキストファイルにして保存します。ContentVersionを使う理由、apex:pageのcontentTypeで直接ダウンロードさせる方法との違い、文字コードの扱い、出力データが増えたときのヒープ上限まで説明します。

2024.06.10

この実装が作るもの

取引先の詳細画面にボタンを置き、押すと取引先名をテキストファイルにして、その取引先に添付します。ファイルはContentVersionとして作り、ContentDocumentLinkで取引先とひもづけます。Attachmentオブジェクトは使いません。Salesforce Filesの仕組みに沿った、いまも通る書き方です。

取引先test1の詳細画面。上部にテキストファイル作成ボタンが置かれている
取引先の詳細画面です。上部に追加した「テキストファイル作成」ボタンから、この処理を呼び出すことを見てください。
取引先アクション「テキストファイル作成」の詳細画面。アクション種別はカスタムVisualforce
ボタンの実体となるアクションの定義です。アクション種別がカスタムVisualforce、Visualforceページが TextFileOutputSample になっている点を見てください。

Visualforceページ

<apex:page showQuickActionVfHeader="false" apiVersion="67.0" standardController="Account" extensions="TextFileOutputSampleController">
    <apex:form>
        <apex:pageBlock title="テキストファイル作成画面">
            <apex:pageBlockTable title="取引先" value="{!acc}" var="acc">
                <apex:column headerValue="取引先名">
                    <apex:outputField value="{!acc.Name}" />
                </apex:column>
            </apex:pageBlockTable>
            <apex:pageBlockButtons location="bottom">
                <div style="width:250px; text-align:center;">
                    <apex:commandButton action="{!createFile}" value="テキストファイル作成" />
                    <apex:commandButton action="{!cancel}" value="キャンセル" />
                </div>
            </apex:pageBlockButtons>
        </apex:pageBlock>
    </apex:form>
</apex:page>

取引先の標準コントローラーを拡張し、ボタンでテキストファイルを作成します。

Apexクラス

public with sharing class TextFileOutputSampleController {
    public Account acc {get; set;}
    private Id accId;

    public TextFileOutputSampleController(ApexPages.StandardController stdController) {
        this.acc = (Account)stdController.getRecord();
        this.accId = this.acc.Id;
        getRecord();
    }

    private void getRecord() {
        this.acc = [SELECT Id, Name FROM Account WHERE Id = :this.accId LIMIT 1];
    }

    public PageReference createFile() {
        String fileContent = '';
        fileContent += this.acc.Name;
        Savepoint sp = Database.setSavepoint();
        try {
            ContentVersion cv = new ContentVersion();
            cv.Title = 'accText.txt';
            cv.PathOnClient = 'accText.txt';
            cv.VersionData = Blob.valueOf(fileContent);
            cv.ContentLocation = 'S';
            insert cv;

            ContentDocumentLink cdLink = new ContentDocumentLink();
            cdLink.ContentDocumentId = [SELECT ContentDocumentId FROM ContentVersion WHERE Id = :cv.Id].ContentDocumentId;
            cdLink.LinkedEntityId = this.accId;
            cdLink.ShareType = 'V';
            insert cdLink;

            return new PageReference('/' + accId);
        } catch (DmlException e) {
            System.debug('The following exception has occurred: ' + e.getMessage());
            Database.rollback(sp);
            return null;
        }
    }

    public PageReference cancel() {
        return new PageReference('/' + this.accId);
    }
}

取引先名からテキストファイルを作り、ContentVersionとして取引先へ添付します。

ContentVersionをContentDocumentIdを指定せずに新規作成すると、対応するContentDocumentが自動的に作られます。直後にContentVersionをIdで引き直してContentDocumentIdを取り出しているのは、この自動生成された値を使ってContentDocumentLinkを作るためです。

取引先のファイル関連リスト。作成されたテキストファイルが1件追加されている
実行したあとの取引先のファイル関連リストです。ContentVersionで作ったファイルが関連リストに増えていることを見てください。
できあがったテキストファイルの中身。取引先名のtest1だけが書かれている
ファイルを開いたところです。取引先名がそのまま1行書き出されていることを確認してください。

apex:pageのcontentTypeで直接ダウンロードさせる方法との違い

この実装はファイルを取引先の「ファイル」関連リストへ保存するもので、ボタンを押した瞬間にブラウザがダウンロードを始めるわけではありません。ブラウザに直接ダウンロードさせたいだけなら、apex:pageのcontentType属性でページ自体をテキストとして出力する方法もあります。

ContentVersionで保存(この記事のコード)apex:pageのcontentTypeで直接出力
出力先取引先に添付されたSalesforceのファイルブラウザのダウンロード
保存後の扱いファイル関連リストからいつでも参照できるページを開くたびに出力するだけで、レコードとしては残らない
向いている場面取引先に証跡として残したいときその場でファイルを受け取れればよいとき
contentType属性はMIMEタイプの指定だけで、文字コード(charset)を切り替える属性は用意されていません。Visualforceページ自体は既定でUTF-8として出力されます。Shift_JISなど他の文字コードでダウンロードさせたい場合、apex:pageの属性だけでは対応できません。

出力データが増えたときの注意

このコードは取引先名1件分をfileContentに連結するだけなので問題になりませんが、複数レコードの値を1つの文字列にまとめて出力するように広げると、fileContentに連結される文字列がどんどん増えていきます。同期処理のApexが使えるヒープサイズは6MBまでです。大きなファイルを作る処理をBatch Apexに移すと、ヒープサイズの上限は12MBに広がります。1つの巨大な文字列を作り続けるのではなく、行ごとのList<String>を作ってString.join()でまとめるなど、ヒープの使い方にも注意してください。

ここで間違えやすい

間違い何が起きるか
Attachmentオブジェクトへファイルを保存する添付ファイルの新しい保存先としては推奨されません。ContentVersionとContentDocumentLinkを使います
contentTypeだけでUTF-8以外の文字コードにしようとする文字コードを切り替える属性が無いため、意図通りになりません
ContentDocumentLinkのShareTypeを確認せずに使う取引先側からファイルを見られる権限が想定と変わります
出力データが増えても1つの文字列に連結し続ける同期処理のヒープサイズ(6MB)に近づきます

確認した環境

  • 2026年9月/Salesforce Summer '26(APIバージョン67.0)時点の公式ドキュメントで、ContentVersionの挙動、apex:pageのcontentType属性、Apexのヒープサイズの上限を確認しています
  • コードはAPIバージョン67.0で書いています

まとめ

  • 取引先へファイルを添付するなら、AttachmentではなくContentVersionとContentDocumentLinkを使います
  • ContentDocumentIdを指定せずにContentVersionを新規作成すると、対応するContentDocumentが自動的に作られます
  • ブラウザへその場でダウンロードさせたいだけなら、apex:pageのcontentType属性で直接出力する方法もあります
  • contentType属性に文字コードを切り替える仕組みはなく、Visualforceページは既定でUTF-8として出力されます
  • 出力データが増える処理では、同期処理のヒープサイズ上限(6MB)を意識し、大きな処理はBatch Apexへ移します

Salesforceの導入・運用についてご相談ください

導入前の検討から、お使いの環境の改修・運用、AIとの連携まで承ります。状況を伺ったうえで、進め方をご提案します。