次の方法で共有


Web API を使用して Azure Blob Storage にファイルをアップロードする

この ガイド は、Azure ファイル API を実装する方法を示しています。 これらの手順により、Web サイトのユーザーまたは訪問者は、Azure に保存されている添付ファイルを表示、編集、作成、削除できるようになります。

注意

  • Azure では最大 10 GB のファイルサイズがサポートされます。
  • この例の手順に従って、列名を変更したり、別のテーブルを使用したりできます。
  • これらのAPIは、すでに作成されているレコードに対してのみ機能します。 これらの API を使用してアップロードされた添付ファイルは、そのレコードにリンクされます。
  • これらの API は、ファイルがチャンク単位でアップロードされると、ノート (注釈) エンティティに対して複数の更新を実行します。

前提条件

このウォークスルーは、File Test という名前のカスタムテーブルに依存しています。 同じ名前のカスタム テーブルを作成することも、別のテーブルを使用することもできます。 カスタム テーブルの作成方法については、Power Appsを使用したテーブルの作成と編集をご参照ください。

ステップ 1: サイト設定の作成

ポータル Azure File API を使用する前に、ポータル管理アプリで必要なサイト設定を有効にする必要があります。 サイト設定は、Azure File API を操作する場合に使用するテーブルによって異なります。

  1. ポータル管理アプリを開きます。

  2. ポータル管理アプリの左側のウィンドウで、サイト設定 を選択します。

  3. 新規を選択します

  4. 名前には、Site/FileManagement/EnableWebAPIを入力します。

  5. Web サイト リストで、Web サイト レコードを選択します。

  6. フィールドに、True と入力します。

  7. 保存して閉じるを選択します。

  8. 次のサイト設定を追加します:

    サイト設定の名前 DefaultValue プロパティ Mandatory
    Site/FileManagement/EnableWebAPI ファイル管理 Web API を有効にします
    Site/FileManagement/BlobStorageAccountName ストレージ アカウント名
    Site/FileManagement/BlobStorageContainerName コンテナー名
    Site/FileManagement/SupportedFileType ファイル タイプのコンマ区切り値。 例: .jpeg、.jpg
    Site/FileManagement/SupportedMimeType ; 区切られた MIME タイプ
    Site/FileManagement/MaxFileSize 1048576 (1 GB) 最大ファイル サイズ (KB 単位) いいえ
    Site/FileManagement/DownloadViaSASUri True (デフォルトで有効化) ファイルのダウンロードに SAS URI を使用するかどうかを決定する方法 いいえ
    Site/FileManagement/DownloadSASUriExpiryTimeSpan 00:10:00 (10 分) SAS URI の最大有効期限 (7 が true の場合に必要) いいえ
    Site/FileManagement/DownloadChunkSizeInKB 4096 (4 MB) ダウンロードするチャンク サイズを決定する方法 (7 が false の場合に必要) いいえ
  9. 注釈 (メモ) 用の Web API を有効にするには、次の追加のサイト設定を追加します

    サイト設定の名前 価値
    Webapi/annotation/enabled
    Webapi/annotation/fields *

ステップ 2: アクセス許可の構成

ユーザーが Azure File API 機能を使用できるように、アクセス許可を構成する必要があります。 この例では、API を使用する新しい Web ロールを設定または作成する必要があります。 次に、ファイル テスト テーブルとメモ テーブルのテーブル権限を追加し、テーブル権限を Web ロールに関連付けます。 最後に、ユーザーが API を使用できるようにするために、Web ロールをユーザーに割り当てます。

注意

API は、認証されたユーザーまたは匿名 Web ロールの Web ロール コンテキストからのテーブル権限に従います。 ユーザーが、API に必要な Web サイト内の特定のテーブルにアクセスできる Web ロールを既に持っているかどうかを検討してください。 API を使用するためだけに追加の Web ロールを作成する必要はありません。

Web ロールを作成する

必要なテーブルへの権限を持つ Web ロールがない場合は、以下の手順を使用して Web ロールを作成し、テーブルの権限を割り当てます。

  1. ポータル管理アプリを開きます。

  2. 左側のペインの セキュリティ セクションで、Web ロールを選択します。

  3. 新規をクリックします。

  4. 名前には、Azure File API User またはこの機能にアクセスするユーザーの役割を最もよく反映した名前を入力します。

  5. Web サイト リストで、Web サイト レコードを選択します。

  6. 保存 を選びます。

テーブルのアクセス許可の作成

  1. Power Pages デザイン スタジオを開きます。

  2. セキュリティワークスペースを選択します。

  3. 保護 セクション配下で、テーブルのアクセス許可 を選択します。

  4. 新しいアクセス許可を選択します。

  5. 名前には、ファイル テスト テーブルの権限を入力します。

  6. テーブル名 リストで、ファイル テスト (cr463_filetest) を選択します。

  7. アクセスの種類リストで、グローバル アクセスを選択します。

  8. 特権の読み取り追加を選択します。

  9. + ロールの追加を選択 し、先ほど作成した Web ロールを選択します。

  10. 保存 を選びます。

  11. 同様の手順で、Note (注釈) テーブルにWriteReadCreateAppend権限を持つ権限をもうひとつ作成し、同じ Web ロールを追加します。

取引先担当者を Web ロールに追加する

  1. ポータル管理アプリを開きます。

  2. 左側のペインの セキュリティ セクションで、取引先担当者を選択します。

  3. この例の Web API で使用する取引先担当者を選択します。

注意

この連絡先は、Web API をテストするためにこの例で使用されるユーザー アカウントです。 必ずポータルで正しい連絡先を選択してください。

  1. 関連>Web ロール を選択します。 ポータルの取引先担当者フォームを使用していることを確認してください。 Power Pages Management アプリを使用する場合、Web ロール サブグリッドは「全般」タブの下部にあります。

  2. 既存の Web ロールの追加を選択します。

  3. 先ほど作成した Azure ファイル API ユーザー ロールを選択します。

  4. 追加を選択します。

  5. 保存して閉じるを選択します。

Microsoft Entra アプリにロール ベースの権限を追加する

  1. ストレージ アカウントを作成した Azure にログインします。

  2. ストレージ アカウントを含むリソース グループに移動します。

  3. アクセス制御 (IAM)>追加>ロール割り当ての追加 を選択します。

  4. リーダーの役割を選択し、次へを選択します。

  5. ユーザー、グループ、またはサービス プリンシパルを選択し、+ メンバーの選択を選択します。

  6. 右側のパネルで、サイト名を検索してポータル エンタープライズ アプリケーションを選択し、選択ボタンを選択します。 アプリケーション名は以下の形式です:

    Portals-<site name>

  7. レビュー+ 割り当て>レビュー+ 割り当て を選択します。

  8. ストレージ アカウントにアクセスし、アクセスの制御 (IAM)>追加>ロールの割り当て追加を選択します。

  9. ストレージ BLOB データ共同作成者 ロールを選択し、次へを選択します。

  10. ユーザー、グループ、またはサービス プリンシパルを選択し、+ メンバーの選択を選択します。

  11. 右側のパネルで、サイト名を検索してポータル エンタープライズ アプリケーションを選択し、選択ボタンを選択します。

  12. レビュー+ 割り当て>レビュー+ 割り当て を選択します。

ステップ 3: Web ページの作成

Azure File API を有効にし、ユーザー権限を構成したので、File Test テーブルのエンティティ リストを含む Web ページを作成します。

  1. Power Pages デザイン スタジオを開きます。

  2. ページ ワークスペースで、+ ページを選択し、その他の方法でページを追加するを選択します。

  3. ページの追加ダイアログで、ページ名ファイルのテスト ページ と入力し、空白から始める を選択します。

  4. 追加を選択します。

  5. リスト を選択し、ファイル テスト テーブルに新しいリストを追加するか、既存のリストを選択します。

    以下のサンプル コードで添付ファイル用のページを作成し、レコードの表示、編集、作成、削除を行います。

  6. Power Pages デザイン スタジオを開きます。

  7. ページ ワークスペースで、+ ページを選択し、その他の方法でページを追加するを選択します。

  8. ページの追加ダイアログで、ページ名添付 と入力し、空白から始める を選択します。

  9. 追加を選択します。

  10. 右上隅にあるコードの編集オプションを選択します。

  11. Visual Studio Code を開く を選択します。

  12. 次のサンプル コード スニペットをコピーして、ページ セクションの <div></div> タグ間に貼り付けます。

    <style>
    .containerforfile
    {
        display:flex;
        margin-bottom:30px; 
    }
    .btn-for-file
    {
        margin-right:10px;
    }
    .file-name
    {
        padding-top:6px;
    }
    .fileinput
    {
        margin-right:10px;
    }
    .container-progress
    {   
         width: 70%;
         max-width: 400px;  
         margin-top: 10px;   
         position: relative;
    } 
    .parent-progress
    {    
        width: 100%;    
        background-color: #2f5fef;    
        height: 30px;    
        margin-top: 25px;    
        margin-bottom: 20px;
    } 
    .child-progress
    {    
        width: 0%;    
        background-color: #53b453;    
        height: 100%;
    } 
    .prog 
    {    position: absolute;  
      display: block;   
       right: 0; 
    }
    
    #attachments{
        font-family: Arial, Helvetica, sans-serif;
        border-collapse: collapse;
        width: 100%;
      }
      #attachments td,
      #attachments th {
        border: 1px solid #ddd;
        padding: 8px;
      }
      #attachments tr:nth-child(even) {
        background-color: #f2f2f2;
      }
      #attachments tr:hover {
        background-color: #ddd;
      }
      #attachments th {
        padding-top: 12px;
        padding-bottom: 12px;
        text-align: left;
        background-color: #2f5fef;
        color: white;
      }
    </style>
    
    <script>
    function selectFile()
    {var child = document.getElementsByClassName("child-progress")[0];
                                    var progSpan = document.getElementsByClassName("prog")[0];
                                        child.style.width = 0 + "%";
                                    progSpan.innerHTML = 0 + "%";
        var elementToChooseFile = document.getElementById("fileinput");
        elementToChooseFile.click();
    }
    function chooseFile()
    {
        var elementToChooseFile = document.getElementById("fileinput");
        var filename =elementToChooseFile.files[0].name;
        var elementforfilename = document.getElementById("filename");
        elementforfilename.innerText = filename;
        uploadFileinChunks();
    }
    
    (function(webapi, $){
                    function safeAjax(ajaxOptions) {
                        var deferredAjax = $.Deferred();
    
                        shell.getTokenDeferred().done(function (token) {
                        // add headers for AJAX
                        if (!ajaxOptions.headers) {
                        $.extend(ajaxOptions, {
                            headers: {
                                "__RequestVerificationToken": token
                            }
                        }); 
                        } else {
                        ajaxOptions.headers["__RequestVerificationToken"] = token;
                    }
                    $.ajax(ajaxOptions)
                        .done(function(data, textStatus, jqXHR) {
                            validateLoginSession(data, textStatus, jqXHR, deferredAjax.resolve);
                        }).fail(deferredAjax.reject); //AJAX
                }).fail(function () {
                    deferredAjax.rejectWith(this, arguments); // on token failure pass the token AJAX and args
                });
    
                return deferredAjax.promise();  
            }
            webapi.safeAjax = safeAjax;
        })(window.webapi = window.webapi || {}, jQuery)
    
    function getFileName(fileName)
    {
        return fileName.replace(/\.azure\.txt$/, '');
    }
    function downloadFile()
    {
        var entityName = document.getElementById("entity_name").value;
        var entityId = document.getElementById("entity_id").value;
        var url = "/_api/file/download/" + entityName + "(" + entityId + ")/blob/$value";
        window.open(url, "_blank");
    }
    
    function uploadFileinChunks()
    {
        var filesizeelement = document.getElementById("filesize");
        var starttimelement = document.getElementById("starttime");
        starttimelement.innerText = new Date().toLocaleString();
        var endtimeelement = document.getElementById("endtime");
        var entityName = "cr463_filetest";
        var entityId = window.location.search.substring(4);
        var url = "/_api/file/InitializeUpload/" + entityName + "(" + entityId + ")/blob"
        var elementToChooseFile = document.getElementById("fileinput");
        var filename = "";
        if (elementToChooseFile.files.length > 0) {
            filename = elementToChooseFile.files[0].name;
        filesizeelement.innerText = elementToChooseFile.files[0].size / 1048576;
            const encodedFileName = encodeURIComponent(filename);
            filename = encodedFileName;
        }
        if (elementToChooseFile.files.length > 0 && elementToChooseFile.files[0].size > 0)
        {
                const chunkSize = 50*1024 *1024;
                let numberOfBlocks;
                let token;
                if (elementToChooseFile.files[0].size % chunkSize == 0)
                {
                    numberOfBlocks = elementToChooseFile.files[0].size / chunkSize;
                }
                else
                {
                    numberOfBlocks = parseInt(elementToChooseFile.files[0].size / chunkSize, 10) + 1;
                }
                webapi.safeAjax({
                    type: "POST",
                    url: url,//replace this with url 
                    headers: { "x-ms-file-name": elementToChooseFile.files[0].name, "x-ms-file-size": elementToChooseFile.files[0].size },
                    contentType: "application/octet-stream",
                    processData: false,
                    data: {},
                    success: function (response, status, xhr)
                    {
                        token = response;
                        uploadFileChunk(0);
                    },
                    error: function (XMLHttpRequest, textStatus, errorThrown)
                    {
                        alert(XMLHttpRequest.responseText);
                    }
                });
    
                function uploadFileChunk(blockno)
                {
                    var fileReader = new FileReader();
    
                    if (blockno < numberOfBlocks)
                    {
                        var end = (blockno * chunkSize + chunkSize) > elementToChooseFile.files[0].size ? blockno * chunkSize + elementToChooseFile.files[0].size % chunkSize : blockno * chunkSize + chunkSize;
                        var content = elementToChooseFile.files[0].slice(blockno * chunkSize, end);
                        fileReader.readAsArrayBuffer(content);
                    }
                    fileReader.onload = function ()
                    {
                        webapi.safeAjax({
                            type: "PUT",
                            url: "/_api/file/UploadBlock/blob?offset=" + (blockno * chunkSize) + "&fileSize=" + elementToChooseFile.files[0].size + "&chunkSize=" + chunkSize + "&token=" + token,
                            headers: { "x-ms-file-name": elementToChooseFile.files[0].name },
                            contentType: "application/octet-stream",
                            processData: false,
                            data: content,
                            success: function (res) {
                                var child = document.getElementsByClassName("child-progress")[0];
                                    var progSpan = document.getElementsByClassName("prog")[0];
                                    var percentComplete = ((parseFloat(end) / parseFloat(elementToChooseFile.files[0].size)) * 100).toFixed(2);
    
                                    child.style.width = percentComplete + "%";
                                    progSpan.innerHTML = percentComplete + "%";
                                    if (percentComplete == 100) {
                                        var table = document.getElementById('attachments');
                                        if(table.hidden)
                                        {
                                        var divForNoAttachment = document.getElementById("no-attachment-found");
                                        divForNoAttachment.hidden = true;
                                        table.hidden = false;
                                        }
                                        var row = document.createElement("tr"); 
                                        row.id = token;
                                        row.innerHTML=`<td><a target="_blank" href="/_api/file/download/annotation(` + token + `)/blob/$value" >` + elementToChooseFile.files[0].name + `</a></td>
                                                <td>`+  new Date().toLocaleString() + `</td>     
                                                <td><button class="btn btn-default" onClick="deleteFile('` + token + `');"><i class="glyphicon glyphicon-trash" aria-hidden="true"></i></button></td>`;
                                        table.firstElementChild.nextElementSibling.appendChild(row);
                                        endtimeelement.innerText = new Date().toLocaleString();
                                    }
                                uploadFileChunk(blockno + 1);
                            },
                            error: function (XMLHttpRequest, textStatus, errorThrown) {
                                alert(XMLHttpRequest.responseText);
                            }
                        });
                    }
                }
        }
        else
        {
        alert("no file chosen");
        }
    }
    
    function loadAllAttachments()
    {
        var fetchXmlQuery = `<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false">
      <entity name="annotation">
        <attribute name="filename" />
        <attribute name="annotationid" />  
        <attribute name="createdon"/>
        <attribute name="objectid" />
        <attribute name="objecttypecode" />
        <filter type="and">
        <condition attribute="filename" operator="like" value="%.azure.txt" />
        </filter>
        <link-entity name="cr463_filetest" from="cr463_filetestid" to="objectid" link-type="inner" alias="ad">
        <filter type="and">
        <condition attribute="cr463_filetestid" operator="eq" value="{` + window.location.search.substring(4) +`}"/>
    </filter>
    </link-entity>
      </entity>
    </fetch>`;
    
    var req = new XMLHttpRequest();
    
    req.open("GET", "/_api/annotations?fetchXml=" +  
        encodeURIComponent(fetchXmlQuery), true);
    
    req.setRequestHeader("Accept", "application/json");
    
    req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    
    req.onreadystatechange = function ()
    {
        if (this.readyState === 4)
        {
            this.onreadystatechange = null;
            if (this.status === 200)
            {
                var returned = JSON.parse(this.responseText);
                var results = returned.value;
                var loading = document.getElementById('loading');
                if (results.length == 0)
                {
                    var divForNoAttachment = document.getElementById("no-attachment-found");
                        divForNoAttachment.hidden = false;
                }
                else
                {
                for (var i = 0; i < results.length; i++)
                {
                    var table = document.getElementById('attachments');
                    if(table.hidden)
                    {
                        var divForNoAttachment = document.getElementById("no-attachment-found");
                        divForNoAttachment.hidden = true;
                        table.hidden = false;
                    }
                    var row = document.createElement("tr"); 
    
                    var fileName = results[i]["filename"];
                    fileName = fileName.replace(".azure.txt", "");
                    var createdOn = results[i]["createdon"];
                    var annotationid = results[i]["annotationid"];
                    row.id = annotationid;
                    row.innerHTML=`<td><a target="_blank" href="/_api/file/download/annotation(` + annotationid + `)/blob/$value" >` + fileName + `</a></td>
                    <td>`+  createdOn + `</td>     
                    <td><button class="btn btn-default" onClick="deleteFile('` + annotationid + `');"><i class="glyphicon glyphicon-trash" aria-hidden="true"></i></button></td>`;
                    table.firstElementChild.nextElementSibling.appendChild(row);
                }
                }
                loading.hidden= true;
            }
            else
            {
                alert(this.status);
            }
        }
    };
    req.send();
    }
    
    document.addEventListener("DOMContentLoaded", function(){
        loadAllAttachments();
    });
    function deleteFile(entityId)
    {
        var entityName = "annotation";
        var url = "/_api/file/delete/" + entityName + "(" + entityId + ")/blob/$value";
        webapi.safeAjax({
            url: url,
            type: "DELETE",
            success: function(){
    
                var row = document.getElementById(entityId);     
                row.parentNode.removeChild(row);
                var table = document.getElementById('attachments');
    
                if(table.hidden == false && table.tBodies[0].children.length == 0)
                { 
                var divForNoAttachment = document.getElementById("no-attachment-found");
                    divForNoAttachment.hidden = false;
                    table.hidden = true;
                }
            },
            error: function (XMLHttpRequest, textStatus, errorThrown)
            {
                alert(XMLHttpRequest.responseText); 
            }
        });
    }
    </script> 
    <div style="margin-left:40px;"> 
    <div class="containerforfile" style="display: flex;"> 
      <input type="file" multiple="true" id="fileinput" onchange="chooseFile()" style="display: none;"> 
      <button type="button" id="button-to-choosefile" onclick="selectFile()" class="btn btn-default btn-for-file">Choose File</button> 
      <div id="filename" class="file-name">No File Selected</div> 
    </div> 
    <br> 
    <div> 
    <label for="filesize" id="file_size_label" class="field-label">FileSize(In MB): </label><div class="filesize" id="filesize"></div> 
    <label for="starttime" id="start_time_label" class="field-label">StartTime:</label><div class="starttime" id="starttime"></div> 
    <label for="endtime" id="end_time_label" class="field-label">EndTime:</label><div class="endtime" id="endtime"></div> 
    </div> 
     <div class="container-progress"> 
         <div class="parent-progress" style="width: 100%;background-color: #c1c1c1;    height: 30px;     margin-top: 25px;    margin-bottom: 20px;">         
            <div class="child-progress" style="width: 0%;    background-color: #53b453;    height: 100%;"></div>    
          </div>            
         <span class="prog">0%</span> 
    </div> 
    <br> 
    <br> 
    <h1>Attachments:</h1> 
    <div id="loading"> Loading Attachments...</div> 
     <div id="no-attachment-found" hidden>No Attachment Found!!</div> 
     <table id="attachments" hidden> 
      <thead> 
        <tr> 
          <th>File</th> 
          <th>Created On</th>       
          <th>Actions</th> 
        </tr> 
      </thead> 
    <tbody> 
    </tbody> 
    </table> 
    </div> 
    
  13. CTRL+S を選択して、コードを保存します。

  14. ファイル テスト ページに戻り、リストを選択し、リストの編集を選択します。

  15. アクション に移動し、レコードの編集を有効にします。

  16. ターゲットの種類 で、Webpage を選択します。

  17. Webpage で、添付 を選択します。

  18. 表示ラベル添付ファイルのアップロードと入力します。

  19. 完了を選択します。

  20. デザイン スタジオの右上隅で、同期を選択して、コードの編集内容をサイトに適用します。

ステップ 4: API を使用して添付ファイルをアップロード、ダウンロード、削除します

Web API 機能をテストするには:

  1. プレビューを選択してからデスクトップを選択します。

  2. 以前に作成した Azure ファイル API ユーザー ロールに割り当てられたユーザー アカウントでサイトにログインします。

  3. 以前作成したファイル テスト ページ の Web ページに移動します。

  4. 画面の右側から添付のアップロードを選択します。

  5. ファイルを選択してアップロードします。

  6. 既存のファイルをダウンロードして削除してみてください。

添付ファイルをアップロード、ダウンロード、削除するサンプル付きの Web ページを作成したので、フォームとレイアウトをカスタマイズすることができます。

エラー コードとメッセージ

次の表には、Web API を使用して Azure にファイルをアップロードする際に発生する可能性のあるさまざまなエラー コードとメッセージが含まれています。

プロパティ HTTP の状態 エラー コード エラー メッセージ
アップロードするファイルが添付されていません 400 FU00001 ファイルの内容が指定されていません
パラメータで指定されたエンティティ ID または名前が正しくありません 404 FU00002 レコードが見つかりません
ユーザーに権限が付与されていません 403 FU00003 この操作を実行するためのアクセス許可がありません
指定されたファイル拡張子が設定されていません 400 FU00004 ファイル拡張子がサポートされていません。
ファイルの MIME がサポートされてません 400 FU00005 ファイルの mimetype がサポートされてません
ファイルサイズが構成されたサイズを超過しています 400 FU00006 ファイル サイズが {0} MB を超えています
Azure の構成が正しくありません 400 FU00007 Azure の構成が正しくありません
ファイル名が指定されていません 400 FU00008 ヘッダー x-ms-file-name が要求にありません
API が有効化されていません 501 FU00009 Azure webapi が有効化されていないため使用できません
更新/アップロードブロック/削除/ダウンロードに有効な Azure ファイルではありません 400 FU00010 要求されたファイルは {0} では利用できません
ストレージ アカウントの Azure で IP 制限が有効になっています 403 FU00011 IP 制限が有効になっています
ダウンロードするファイルが Azure に存在しません 400 FU00012 ファイルが存在しません
チャンク サイズが100 MB を超えています 400 FU00013 チャンク サイズが100 MB を超えています
ファイル サイズが指定されていません 400 FU00014 ファイル サイズが指定されていません
サポートされるファイルの種類が設定されていません 400 FU00015 ファイルの拡張子がサポートされていません
サポートされる MIME が設定されていません 400 FU00016 ファイル mimetype はサポートされていません
チャンクなしで 128 MB を超過しています 400 FU00017 ファイルサイズが 128MB を超えています
Microsoft Entra アプリにアクセス許可が付与されていません 403 FU00018 Microsoft Entra アプリケーションにこの操作を実行するアクセス許可がありません
Azure との接続を作成するための証明書が存在しません 400 FU00019 証明書が存在しません
アプリ設定でテナント ID、クライアント ID、またはサムプリントが設定されていない場合 500 FU00020 内部サーバー エラー
ファイルの MIME タイプ/拡張子がサポートされていません 400 FU00021 ファイルの MIME タイプまたは拡張子がサポートされていません
アカウント名またはコンテナ名が存在しないか、指定されていません 400 FU00022 Azure の構成が正しくありません