【Salesforce】apex:inputFile can not be used in conjunction with an action component, apex:commandButton or apex:commandLink that specifies a rerender or oncomplete attribute.

【Salesforce】apex:inputFile can not be used in conjunction with an action component, apex:commandButton or apex:commandLink that specifies a rerender or oncomplete attribute.

Visualforceで「apex:inputFile」タグを使用した際に、このようなエラーが出ました。

apex:inputFile can not be used in conjunction with an action component, apex:commandButton or apex:commandLink that specifies a rerender or oncomplete attribute. 

rerenderを使っているところに「apex:inputFile」を使うことはできないようです。

単純にファイルをアップロードするだけであれば便利なタグなのですが、微妙に使いづらいですね。

どうしても使うことができないようなので、HTMLタグの「input type=”file”」を使用することで解決しました。

javascript側の処理にはFileReaderオブジェクトを使用しています。

https://developer.mozilla.org/ja/docs/Web/API/FileReader

また、Base64状態の文字列を復号化する必要もあります。

https://developer.salesforce.com/docs/atlas.ja-jp.204.0.apexcode.meta/apexcode/apex_classes_restful_encodingUtil.htm

Apex側です。

ファイル添付用のクラスを作り、Visualforce側からString(Base64)でデータを取得しています。

public Account account{get;set;}
public FileAttachment fileAttachment{get;set;}

/**
 * ファイルをアップロードする
 */
public void uploadAttachment(){

    // 添付ファイルを取得する
    Attachment attachment = this.fileAttachment.getAttachment();
    // ファイルが添付されている場合はinsertする
    if(attachment.Body != null){
    
        attachment.ParentId = this.account.Id;
        insert attachment;
    }
    
    // 添付ファイルを初期化する
    this.fileAttachment = new FileAttachment();
}

/**
 * ファイルアップロード用の内部クラス
 * input type="file"を利用して添付ファイルを取得する
 */
public class FileAttachment{

    private Attachment attachment;

    /**
     * コンストラクタ
     */
    public FileAttachment(){

        this.attachment = new Attachment();
    }

    /** 添付ファイル */
    public Attachment getAttachment(){

        return this.attachment;
    }
    public void setAttachment(Attachment param){

        this.attachment = param;
    }

    /** ファイル名 */
    public String getName(){

        return this.attachment.Name;
    }
    public void setName(String param){

        this.attachment.Name = param;
    }

    /** ファイル内容 */
    public String getBody(){

        return null;
    }
    public void setBody(String param){

        Blob b = null;

        // 中身が設定されている場合
        if(String.isNotEmpty(param)){

            String dat = param.substringAfterLast(',');
            b = EncodingUtil.base64Decode(dat);
        }

        // 添付ファイルのBodyに設定する
        this.attachment.Body = b;
    }
}

重要なのはsetBodyメソッドくらいでしょうか。

ファイルが設定されている場合に、取得された文字列からファイル部分を取得しています。

ちなみに、paramはこのような形式で取得されています。

最後のカンマから後ろがデータになっていますね。

「data:text/plain;base64,44OG44K544OI55So5paH5a2X5YiX」

Visualforce側です。

ファイルが設定された際に、javascriptでその内容をコントローラの変数に渡しています。

javascript部分はjQueryで楽をしています。

<input type="file" onchange="setAttachment(this);" />
<apex:inputHidden id="fileName" value="{!fileAttachment.Name}" />
<apex:inputHidden id="fileBody" value="{!fileAttachment.Body}" />

<apex:commandButton action="{!uploadAttachment}" value="アップロード" />

<script>
function setAttachment(obj){

    var reader = new FileReader();
    var file= obj.files[0];
    var fileName;
    var fileBody;

    // ファイルが設定されていない場合は何もしない
    if(typeof file === "undefined"){

        return;
    }

    // ファイル読み込み時の処理を設定する
    reader.onload = function(event){

        // ファイル名を取得する
        fileName = file.name;
        // ファイルの中身を取得する
        fileBody = event.target.result;
        // hidden項目にNameを設定する
        $(obj).siblings('input[id$="fileName"]').val(fileName);
        // hidden項目にBodyを設定する
        $(obj).siblings('input[id$="fileBody"]').val(fileBody);
    }

    // ファイルの読み込みを行う
    reader.readAsDataURL(file);
}
</script>

このような記述をすることで、Visualforcタグを使用せずにファイルをアップロードすることができます。

ファイルの保存後に添付ファイル用クラスを初期化していますが、あれがないとViewStateのガバナで落ちたりします。

また、この方法はFileReaderオブジェクトを使用しているため対応していないブラウザでは動作しません。

古いブラウザではどのような方法でやるのでしょうか。

私には分かりません。

もう少し綺麗なコードを書けると良いのですが、まだまだ技術不足ですね。

 

2018/11/07 追記

上の方法でアップロードすると、ブラウザによっては画像が切れてしまうことが分かりました。

回避策を記事にしたので、気が向いたらご一読ください。

http://www.subnetwork.jp/blog/?p=1210

No comments.

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です