ファイルを送受信する

エージェントは、Microsoft Teams の会話でドキュメントを受信、読み取り、送信できます。 Teams はこれらのファイルを OneDrive または SharePoint に保存し、ファイル カードまたは保存済みアイテムとして表示します。

Teams SDK を使用して、個人用チャットでファイルを受信し、ボット ファイルの同意によってファイルを送信します。 エージェントが個人用チャット、グループ チャット、チャネルにわたって保存されたファイルを操作する必要がある場合は、Microsoft Graph を使用します。 会話で直接レンダリングされる画像については、「 インライン イメージの送受信」を参照してください。

重要

エージェントは、Government Community Cloud High (GCC High)、国防総省 (DoD)、または 21Vianet が運用するチームでのドキュメント ファイルの送受信ワークフローをサポートしていません。

ユーザー エクスペリエンス

ユーザーがエージェントとドキュメントを交換する方法を理解して、明確なプロンプト、同意要求、確認を提供できるようにします。 ユーザーは次のことができます。

  • エージェントとの個人用チャットにドキュメントを添付します。
  • エージェントが提供するファイルを確認して承諾または拒否します。
  • 正常にアップロードされたファイルをファイル カードから開きます。
  • サポートされている会話範囲全体で、保存されている OneDrive または SharePoint ファイルを操作します。

受信したドキュメントは OneDrive または SharePoint に保存され、ファイル カードまたは保存済みアイテムとして表示されます。 個人チャットでファイルを送信するボットは、最初に同意を求め、承認後にファイルをアップロードした後、ファイル情報カードを送信します。

開発者のエクスペリエンス

アプリの ID、方向、および会話スコープに基づいてファイル API を選択します。 次のオプションは、この記事の実装順序と一致します。

要件 推奨されるアプローチ
個人用チャットでドキュメントを受信して読む Teams SDK ファイル アクセサー
スレッド スコープ全体で保存されたファイルを送信または取得する Microsoft Graph
個人用チャットでボットからドキュメントを送信する Teams SDK ファイルの同意

Teams SDK ファイル アクセサーは、アクティビティ添付ファイル コレクションをフィルター処理し、サポートされているドキュメントを遅延受信ファイル ハンドルとして公開します。 エージェント ユーザーは、自分の ID を使用して Microsoft Graph を通じてファイル バイトを取得します。 従来のボットは、Teams によって提供される有効期間が短く、事前承認された URL を使用します。

操作 個人用チャット グループ チャット チャネル 要件
Teams SDK ファイル アクセサーを使用してファイルを受信する サポート 明示的にサポートされていません 明示的にサポートされていません エージェント ユーザーには、Graph ファイルのアクセス許可が必要です。 ボットは事前承認済みの URL を使用します。
Graph を使用してファイルを送信または取得する サポート サポート サポート ストレージの場所、API、呼び出し元 ID、アクセス モデルのアクセス許可を構成します。
ボット ファイルの同意を使用してファイルを送信する サポート サポート対象外 サポート対象外 ボット エントリで supportsFiles を true に設定します。

ファイルの受信

ユーザーが個人用チャットに添付するドキュメント ファイルを受信して、エージェントがそのコンテンツを調べたり処理したりできるようにします。 最初にファイル アクセスを構成してから、Teams SDK ファイル アクセサーを使用して、受信した各ファイルを一覧表示し、読み取ります。

ファイル サポートの構成

エージェントまたはボットがドキュメントを受信するために必要なアクセス許可とプラットフォーム設定を持つように、ファイル サポートを構成します。 ファイル ハンドラーを実装する前に、アプリ ID のセットアップを完了してください。

必要な構成は、id とファイルの操作によって異なります。

シナリオ アプリ マニフェストの更新 その他の構成
エージェント ユーザーがファイルを受け取る なし エージェント ブループリントに Microsoft Graph ファイルのアクセス許可を追加し、管理者の同意を得てください。
ボットがファイルの同意を通じてファイルを受信または送信する ボット エントリで supportsFiles を true に設定します。 Teams によって提供される事前承認済みファイル URL の場合は なし。
アプリが Microsoft Graph で保存されているファイルを送信または取得する ファイル固有のアプリ マニフェスト プロパティがないようにする 呼び出し元に必要な Microsoft Graph のアクセス許可と認証を構成します。

エージェント ユーザー

エージェント ユーザーの場合、 supportsFiles を設定したり、別のファイル固有のアプリ マニフェスト プロパティを追加したりする必要はありません。 エージェント ブループリントに対する Microsoft Graph ファイルのアクセス許可を構成し、管理者の同意を得てください。 エージェント ユーザーは、独自の ID を使用して Microsoft Graph を通してファイル コンテンツを取得します。 詳細については、「 継承可能なアクセス許可」を参照してください。

重要

ファイルの取得をテストする前に、ブループリントのアクセス許可を構成して同意します。 Graph 資格情報がないと、SDK はファイル資格情報エラーを発生させます。

Bot

ファイルを受信するボット、またはファイルの同意を使用するボットの場合は、アプリ マニフェストのボット エントリで supportsFiles を true に設定します。

{
  "bots": [
    {
      "botId": "${{BOT_ID}}",
      "scopes": ["personal"],
      "supportsFiles": true
    }
  ]
}

キー値:

  • botId: ボット登録を識別するように ${{BOT_ID}} を設定します。
  • scopes: personal を追加して、1 対 1 のファイル操作を有効にします。
  • supportsFiles: 添付ファイル コントロールを表示する true を設定します。

supportsFiles: true がない場合、ユーザーはボットとの個人チャットでファイルを添付することができず、ファイル アクセサーは空のコレクションを返します。 この設定では、Microsoft Graph のアクセス許可は付与されません。

受信したファイルとメタデータにアクセスする

ユーザーが個人用チャットにドキュメントを添付すると、Teams はそれを OneDrive または SharePoint に保存します。 メッセージ アクティビティにはファイル メタデータが含まれており、Teams SDK ファイル アクセサーはファイル バイトへの遅延アクセスを提供します。

ListAsync() を使用して、現在のメッセージに添付されているサポートされているすべてのファイルにアクセスします。

teamsApp.OnMessage(async (context, cancellationToken) =>
{
    IList<IncomingFile> files =
        await context.Files.ListAsync(cancellationToken);

    if (files.Count == 0)
    {
        await context.ReplyAsync(
            "Attach a file and I will read it.",
            cancellationToken);
        return;
    }

    string names = string.Join(", ", files.Select(file => file.Name));
    await context.ReplyAsync(
        $"You sent {files.Count} file(s): {names}",
        cancellationToken);
});

主要な API と値:

  • context.Files.ListAsync: ファイル バイトをダウンロードせずにファイル メタデータを返します。
  • file.Name: アップローダーが指定した名前を読み取る場合は表示のみです。

ListAsync() 添付ファイルの順序を保持します。 サポートされているファイルのないアクティビティの空のコレクションを返し、不正な形式のファイル エントリをスキップします。 ハンドラーが 1 つのファイルを想定している場合は、 FirstAsync() を使用します。

IncomingFile? file =
    await context.Files.FirstAsync(cancellationToken);

if (file is not null)
{
    await context.ReplyAsync(
        $"Reading {file.Name}...",
        cancellationToken);
}

主要な API と値:

  • FirstAsync: サポートされている最初のファイルまたは nullを返します。
  • file.Name: 受信したファイルのアップローダーが指定した名前を表示します。

list() を使用して、現在のメッセージに添付されているサポートされているすべてのファイルにアクセスします。

app.on('message', async ({ files, send }) => {
  const attached = await files.list();

  if (attached.length === 0) {
    await send('Attach a file and I will read it.');
    return;
  }

  const names = attached.map((file) => file.name).join(', ');
  await send(`You sent ${attached.length} file(s): ${names}`);
});

主要な API と値:

  • files.list: ファイル バイトをダウンロードせずにファイル メタデータを返します。
  • file.name: アップローダーが指定した名前を読み取る場合は表示のみです。

list() 添付ファイルの順序を保持します。 サポートされているファイルのないアクティビティの場合は空の配列を返し、不正な形式のファイル エントリをスキップします。 ハンドラーが 1 つのファイルを想定している場合は、 first() を使用します。

const file = await files.first();

if (file) {
  await send(`Reading ${file.name}...`);
}

主要な API と値:

  • first: サポートされている最初のファイルまたは undefinedを返します。
  • file.name: 受信したファイルのアップローダーが指定した名前を表示します。

list() を使用して、現在のメッセージに添付されているサポートされているすべてのファイルにアクセスします。

@app.on_message
async def handle_message(ctx: ActivityContext[MessageActivity]) -> None:
    attached = await ctx.files.list()

    if not attached:
        await ctx.reply("Attach a file and I will read it.")
        return

    names = ", ".join(file.name for file in attached)
    await ctx.reply(f"You sent {len(attached)} file(s): {names}")

主要な API と値:

  • ctx.files.list: ファイル バイトをダウンロードせずにファイル メタデータを返します。
  • file.name: アップローダーが指定した名前を読み取る場合は表示のみです。

list() 添付ファイルの順序を保持します。 サポートされているファイルのないアクティビティの空のリストを返し、不正な形式のファイル エントリをスキップします。 ハンドラーが 1 つのファイルを想定している場合は、 first() を使用します。

file = await ctx.files.first()

if file:
    await ctx.reply(f"Reading {file.name}...")

主要な API と値:

  • first: サポートされている最初のファイルまたは Noneを返します。
  • file.name: 受信したファイルのアップローダーが指定した名前を表示します。

受信ファイル ハンドルには次のものが含まれます。

  • 一意の ID: OneDrive または SharePoint ドライブアイテム ID (使用可能な場合)。
  • 名前: アップローダーが指定したファイル名 (ファイル拡張子を含む)。
  • 拡張機能: 先頭期間のないプラットフォームが提供する拡張機能。
  • コンテンツ タイプ: ソースによって提供される場合の MIME タイプ。
  • スコープ: ファイルが受信された会話スコープ。
  • ソース: 受信ファイルを識別した SDK ソース。
  • コンテンツ URL: 参照可能なストレージ URL ですが、必ずしもダウンロード URL ではありません。
  • 未加工の添付ファイル: プロトコル レベルの診断用の元のメタデータ。

注意

Name信頼されていない入力として扱います。 ファイルを書き込む前に、ファイルを安全なアプリケーション生成名に置き換えるか、ファイルをサニタイズして、解決された宛先がアプリケーション制御ディレクトリ内に残っていることを確認してください。

受信したファイルの読み取り

アプリが処理、保存、または分析のためにテキストまたはバイナリ コンテンツを必要とするときに、受信したファイルを読み取ります。 再利用可能なインメモリ コピーにはダウンロード方法を使用するか、大きなファイルにはストリーミング方法を選択します。

DownloadAsync() を使用して、再利用可能なメモリ内コピーにファイルをダウンロードします。

IncomingFile? file =
    await context.Files.FirstAsync(cancellationToken);

if (file is not null)
{
    DownloadedFile downloaded =
        await file.DownloadAsync(cancellationToken);

    await context.ReplyAsync(
        $"Downloaded {downloaded.Filename} " +
        $"({downloaded.Bytes.Length} bytes, {downloaded.ContentType}).",
        cancellationToken);
}

主要な API と値:

  • DownloadAsync: 再利用可能なバイト コピー 1 つを取得してバッファーに格納します。
  • downloaded.Bytes: ダウンロードしたファイルの完全なバイナリ コンテンツにアクセスします。
  • downloaded.ContentType: 解決済みの MIME の種類を処理に使用します。
  • downloaded.Filename: 解決された名前を表示目的で使用します。

その他の読み取りオプションは次のとおりです。

  • TextAsync(): 既定では、テキストを UTF-8 としてダウンロードおよびデコードします。
  • StreamAsync(): 大きなファイルを完全にバッファリングせずに処理します。
  • SaveAsAsync(path): バイトを安全なローカル パスに直接Streamします。

TextAsync() 無効なバイト シーケンスを U+FFFD に置き換えます。 UTF-8 以外のテキスト用に Encoding を提供し、バイナリ ファイルをバイトまたはストリームとして処理します。

IncomingFileはバイトをキャッシュしません。 DownloadAsync()、TextAsync()、StreamAsync()、または SaveAsAsync() を呼び出すたびに、別のネットワーク要求が実行されます。 同じコンテンツを 1 回ダウンロードして、 DownloadedFile を複数回必要になったときに再利用してください。

DownloadedFile downloaded =
    await file.DownloadAsync(cancellationToken);

string text = downloaded.Text();
byte[] bytes = downloaded.Bytes;

await downloaded.SaveAsAsync(
    "./downloads/copy.bin",
    cancellationToken);

主要な API と値:

  • downloaded.Text(): バッファーされたコピーを、別のダウンロードを必要とせずにデコードします。
  • downloaded.Bytes: バッファーされたバイトをバイナリ処理に再利用します。
  • SaveAsAsync: ファイルを再度フェッチせずに、バッファーされたバイトを保存します。

download() を使用して、再利用可能なメモリ内コピーにファイルをダウンロードします。

const file = await files.first();

if (file) {
  const downloaded = await file.download();
  await send(
    `Downloaded ${downloaded.filename} ` +
    `(${downloaded.bytes.length} bytes, ${downloaded.contentType}).`
  );
}

主要な API と値:

  • download: 再利用可能なバイト コピー 1 つを取得してバッファーに格納します。
  • downloaded.bytes: ダウンロードしたファイルのバイナリ コンテンツにアクセスします。
  • downloaded.contentType: 解決済みの MIME の種類を処理に使用します。
  • downloaded.filename: 解決された名前を表示目的で使用します。

その他の読み取りオプションは次のとおりです。

  • text(): 既定では、テキストを UTF-8 としてダウンロードおよびデコードします。
  • stream(): 大きなファイルを完全にバッファリングせずに処理します。
  • saveAs(path): バイトを安全なローカル パスに直接Streamします。

受信ファイルはバイトをキャッシュしません。 同じコンテンツを 1 回ダウンロードして、ダウンロードしたファイルを複数回必要になったときに再利用します。

const downloaded = await file.download();
const text = downloaded.text();
const buffer = downloaded.arrayBuffer();

await downloaded.saveAs('./downloads/copy.bin');

主要な API と値:

  • downloaded.text(): バッファーされたコピーを、別のダウンロードを必要とせずにデコードします。
  • downloaded.arrayBuffer(): バッファーされたバイトをバイナリ処理に再利用します。
  • saveAs: ファイルを再度フェッチせずに、バッファーされたバイトを保存します。

download() を使用して、再利用可能なメモリ内コピーにファイルをダウンロードします。

file = await ctx.files.first()

if file:
    downloaded = await file.download()
    await ctx.reply(
        f"Downloaded {downloaded.filename} "
        f"({len(downloaded.bytes)} bytes, {downloaded.content_type})."
    )

主要な API と値:

  • download: 再利用可能なバイト コピー 1 つを取得してバッファーに格納します。
  • downloaded.bytes: ダウンロードしたファイルのバイナリ コンテンツにアクセスします。
  • downloaded.content_type: 解決済みの MIME の種類を処理に使用します。
  • downloaded.filename: 解決された名前を表示目的で使用します。

その他の読み取りオプションは次のとおりです。

  • text(): 既定では、テキストを UTF-8 としてダウンロードおよびデコードします。
  • stream(): 大きなファイルを完全にバッファリングせずに処理します。
  • save_as(path): バイトを安全なローカル パスに直接Streamします。

受信ファイルはバイトをキャッシュしません。 同じコンテンツを 1 回ダウンロードして、ダウンロードしたファイルを複数回必要になったときに再利用します。

downloaded = await file.download()
text = downloaded.text()
data = downloaded.bytes

await downloaded.save_as("./downloads/copy.bin")

主要な API と値:

  • downloaded.text(): バッファーされたコピーを、別のダウンロードを必要とせずにデコードします。
  • downloaded.bytes: バッファーされたバイトをバイナリ処理に再利用します。
  • save_as: ファイルを再度フェッチせずに、バッファーされたバイトを保存します。

エージェント ユーザーの場合、Teams SDK は添付ファイル ContentUrl とエージェント ユーザーの ID を使用して、Microsoft Graph を介してファイルを取得します。 ボットの場合、SDK はアクティビティで提供される有効期間の短い事前承認済み downloadUrl を使用します。 このセクションに示すファイル読み取り API は、両方のルートに適用されます。

未加工の添付ファイルにアクセスする

診断やトラブルシューティングなど、型指定されたファイル アクセサーが公開しないプロトコル メタデータが必要な場合は、生の添付ファイルにアクセスします。 必要な場合にのみ生のペイロードを検査し、明示的に選択された機密でないフィールドのみをログに記録します。

IncomingFile.Raw は、元のプロトコル ペイロードが必要な場合にのみ使用してください。 次の例では、オブジェクト全体をシリアル化するのではなく、添付ファイル名とコンテンツ タイプをログに記録します。

IncomingFile? file =
    await context.Files.FirstAsync(cancellationToken);

if (file is not null)
{
    logger.LogDebug(
        "File attachment received. Name: {Name}; Content type: {ContentType}",
        file.Raw.Name,
        file.Raw.ContentType);
}

主要な API と値:

  • file.Raw: 診断またはトラブルシューティングのために元のメタデータにアクセスします。
  • file.Raw.Name: 秘密度を検証した後にのみ、表示名をログに記録します。
  • file.Raw.ContentType: URL を公開せずに、添付ファイルの種類をログに記録します。
  • logger.LogDebug: 完全なペイロードではなく、選択したメタデータを記録します。

IIncomingFile.raw は、元のプロトコル ペイロードが必要な場合にのみ使用してください。 次の例では、完全なオブジェクトではなく、添付ファイル名とコンテンツ タイプをログに記録します。

const file = await files.first();

if (file) {
  log.debug('File attachment received', {
    name: file.raw.name,
    contentType: file.raw.contentType
  });
}

主要な API と値:

  • file.raw: 診断またはトラブルシューティングのために元のメタデータにアクセスします。
  • file.raw.name: 秘密度を検証した後にのみ、表示名をログに記録します。
  • file.raw.contentType: URL を公開せずに、添付ファイルの種類をログに記録します。
  • log.debug: 完全なペイロードではなく、選択したメタデータを記録します。

IncomingFile.raw は、元のプロトコル ペイロードが必要な場合にのみ使用してください。 次の例では、完全なオブジェクトではなく、添付ファイル名とコンテンツ タイプをログに記録します。

file = await ctx.files.first()

if file:
    ctx.logger.debug(
        "File attachment received. Name: %s; Content type: %s",
        file.raw.name,
        file.raw.content_type,
    )

主要な API と値:

  • file.raw: 診断またはトラブルシューティングのために元のメタデータにアクセスします。
  • file.raw.name: 秘密度を検証した後にのみ、表示名をログに記録します。
  • file.raw.content_type: URL を公開せずに、添付ファイルの種類をログに記録します。
  • ctx.logger.debug: 完全なペイロードではなく、選択したメタデータを記録します。

未加工の添付ファイル URL、識別子、または完全なペイロードはログに記録しないでください。 ファイル アクセサーは、インライン イメージ、カード、メンション、リンク プレビュー、HTML 添付ファイル、または不正な形式のファイル エントリを返しません。 アクティビティの添付ファイル コレクションからこれらのアイテムにアクセスします。 画像固有のガイダンスについては、「 インライン イメージの送受信」を参照してください。

ファイルの送信

アプリで生成されたレポート、エクスポート、またはその他のダウンロード可能なコンテンツを配信する必要がある場合は、保存したドキュメントをユーザーに送信します。 エージェントとクロススコープのシナリオには Microsoft Graph を使用するか、個人チャットでボット ファイルの同意を使用します。

保存ファイルに Microsoft Graph を使用する

アプリが個人チャット、グループ チャット、またはチャネル間で保存済みファイルを送信または取得する必要がある場合は、Microsoft Graph を使用します。

  • 個人用ファイルとグループ チャット ファイルにはユーザーの OneDrive を使用します。
  • チャネル ファイルには、チームの SharePoint サイトを使用します。
  • OAuth 2.0 を使用して、必要なストレージ アクセス権を取得します。
  • 既存の保存ファイルを参照するメッセージの添付ファイルを投稿します。

詳細については、「 チャット メッセージの添付ファイルを送信する」 および 「OneDrive および SharePoint API」を参照してください。

ボットのファイル同意ワークフローを使用して、ボットがユーザーの OneDrive にドキュメントをアップロードする前にアクセス許可を要求します。 個人用チャットに次のシーケンスを実装します。

  1. FileConsentCardを送信します。
  2. fileConsent/invoke アクティビティを受け取ります。
  3. ユーザーが受け入れた場合は、HTTP PUTを使用してuploadUrlにバイトをアップロードします。
  4. アップロードしたファイルにリンクする FileInfoCard を送信します。
  5. ユーザーが拒否した場合は、保留中のコンテンツを破棄します。

アプリが OneDrive にアップロードする前に、ユーザーがファイル名、目的、サイズを確認できるように同意を要求します。 FileConsentCardを送信し、ユーザーが受け入れるか拒否するまで、保留中のファイル コンテンツを保持します。

次のメッセージは、ファイルのアップロード許可を要求します。

ファイルのアップロード許可を要求する同意カード。

モバイルでは、同意要求は次のように表示されます。

モバイルにファイルをアップロードする許可を要求する同意カード。
{
  "attachments": [
    {
      "contentType": "application/vnd.microsoft.teams.card.file.consent",
      "name": "file_example.txt",
      "content": {
        "description": "This is your monthly expense report.",
        "sizeInBytes": 1029393,
        "acceptContext": {
          "fileId": "expense-report-2026-09"
        },
        "declineContext": {
          "fileId": "expense-report-2026-09"
        }
      }
    }
  ]
}

キーのプロパティと値:

  • contentType: 表示されているファイル同意カードのコンテンツの種類を使用します。
  • name: Teams がユーザーに表示するファイル名を設定します。
  • description: 提供されたファイルの簡単な目的を追加します。
  • sizeInBytes: 正確なファイル サイズをバイト単位で設定します。
  • acceptContext: ユーザーが同意した後に必要な識別子を追加します。
  • declineContext: ユーザーが拒否した場合に必要な識別子を追加します。

保留中のファイル バイトはコンテキスト オブジェクトの外部に保持します。 保留中のアップロードに有効期限ポリシーとクリーンアップ ポリシーを適用します。

承認または拒否を処理する

同意応答を処理して、保留中のファイルをアップロードするか破棄するかを決定します。 fileConsent/invoke アクションを検査し、保留中のコンテンツを見つけるために必要な識別子のみを保持します。

ユーザーが受け入れると、Teams はactionを accept に設定し、uploadInfo.uploadUrlfileConsent/invokeを送信します。 ユーザーが拒否した場合、 action は declineされます。

ファイル コンテンツのアップロード

保留中のバイトは、ユーザーがファイルを受け入れた後にのみアップロードしてください。 指定されたアップロード URL、必要なバイト範囲ヘッダー、および HTTP PUTを使用して、OneDrive が成功ステータスを返すことを確認します。

アップロード URL を使用してファイル バイトを転送します。

using System.Net;
using System.Net.Http.Headers;

async Task UploadToOneDrive(
    string url,
    byte[] content,
    CancellationToken cancellationToken)
{
    if (content.Length == 0)
    {
        throw new ArgumentException(
            "The file content must not be empty.",
            nameof(content));
    }

    using var request = new HttpRequestMessage(HttpMethod.Put, url);
    request.Content = new ByteArrayContent(content);
    request.Content.Headers.ContentType =
        new MediaTypeHeaderValue("application/octet-stream");
    request.Content.Headers.ContentLength = content.Length;
    request.Content.Headers.ContentRange =
        new ContentRangeHeaderValue(0, content.Length - 1, content.Length);

    using HttpResponseMessage response =
        await httpClient.SendAsync(request, cancellationToken);

    if (response.StatusCode is not HttpStatusCode.OK
        and not HttpStatusCode.Created)
    {
        throw new HttpRequestException(
            $"Upload failed with status {response.StatusCode}.");
    }
}

キー パラメーターと値:

  • url: 承諾後に返される UploadInfo.UploadUrl を使用します。
  • content: 同意に関連付けられている正確なバイト数を指定します。
  • content.Length: バイト範囲を作成する前に空のアップロードを拒否します。
  • ContentType: バイナリ転送の application/octet-stream を設定します。
  • ContentLength: アップロードされるバイト数の正確な数を設定します。
  • ContentRange: アップロードされたバイト範囲と合計を指定します。
  • httpClient.SendAsync: HTTP PUT 要求を OneDrive に送信します。
  • response.StatusCode: HTTP 200 または 201 を受け入れ、それ以外の場合は失敗します。
import axios from 'axios';

async function uploadToOneDrive(url: string, content: Buffer): Promise<void> {
  if (content.length === 0) {
    throw new Error('The file content must not be empty.');
  }

  const fileSize = content.length;
  const response = await axios.put(url, content, {
    headers: {
      'Content-Type': 'application/octet-stream',
      'Content-Length': fileSize.toString(),
      'Content-Range': `bytes 0-${fileSize - 1}/${fileSize}`
    }
  });

  if (![200, 201].includes(response.status)) {
    throw new Error(`Upload failed with status ${response.status}`);
  }
}

キー パラメーターと値:

  • url: 承諾後に返される uploadInfo.uploadUrl を使用します。
  • content: 同意に関連付けられている正確なバイト数を指定します。
  • content.length: バイト範囲を作成する前に空のアップロードを拒否します。
  • Content-Type: バイナリ ファイル転送の application/octet-stream を設定します。
  • Content-Length: アップロードされるバイトの 10 進数の長さを設定します。
  • Content-Range: アップロードされたバイト範囲と合計を指定します。
  • axios.put: HTTP PUT 要求を使用してバイトをアップロードします。
  • response.status: HTTP 200 または 201 を受け入れ、他の応答で失敗します。
import httpx

async def upload_to_onedrive(url: str, content: bytes) -> None:
    if not content:
        raise ValueError("The file content must not be empty.")

    file_size = len(content)
    headers = {
        "Content-Type": "application/octet-stream",
        "Content-Length": str(file_size),
        "Content-Range": f"bytes 0-{file_size - 1}/{file_size}",
    }

    async with httpx.AsyncClient() as client:
        response = await client.put(url, content=content, headers=headers)

    if response.status_code not in (200, 201):
        raise RuntimeError(
            f"Upload failed with status {response.status_code}"
        )

キー パラメーターと値:

  • url: 承諾後に返される upload_info.upload_url を使用します。
  • content: 同意に関連付けられている正確なバイト数を指定します。
  • if not content: バイト範囲を作成する前に空のアップロードを拒否します。
  • Content-Type: バイナリ転送の application/octet-stream を設定します。
  • Content-Length: アップロードされるバイト数の正確な数を設定します。
  • Content-Range: アップロードされたバイト範囲と合計を指定します。
  • httpx.AsyncClient.put: HTTP PUT 要求を使用してバイトをアップロードします。
  • response.status_code: HTTP 200 または 201 を受け入れ、それ以外の場合は失敗します。

ユーザーに通知する

アップロードが成功したら、保存したファイルを開いたりダウンロードしたりできるようにユーザーに通知します。 ドライブ項目識別子、ファイルの種類、保存されているファイルの URL を含む FileInfoCard を送信します。

アップロードが成功したら、ファイル情報の添付ファイルを送信します。

{
  "attachments": [
    {
      "contentType": "application/vnd.microsoft.teams.card.file.info",
      "contentUrl": "https://contoso.sharepoint.com/personal/user/Documents/Applications/file_example.txt",
      "name": "file_example.txt",
      "content": {
        "uniqueId": "1150D938-8870-4044-9F2C-5BBDEBA70C8C",
        "fileType": "txt"
      }
    }
  ]
}

キーのプロパティと値:

  • contentType: 表示されているファイル情報カードのコンテンツの種類を使用します。
  • contentUrl: ユーザーがアクセスできる保存ファイルの URL を設定します。
  • uniqueId: OneDrive または SharePoint ドライブアイテム ID を設定します。
  • fileType: プラットフォームによって報告されるファイル拡張子を句読点なしで設定します。

エラーを処理する

既知の SDK ファイル エラーを、トランスポートやサービスのエラーとは切り離して処理します。 エージェント ユーザーの場合は、ファイルの取得はエージェント ID と同意されたブループリントのアクセス許可に依存するため、資格情報と Graph アクセス エラーを最初に処理します。 期限切れ URL エラーは、事前承認されたダウンロード URL を使用するボット パスに適用されます。

状態コード エラー コード 説明 開発者の処置
該当なし FileCredentialException エージェント ユーザー ファイルの取得に使用できる Graph 資格情報はありません。 ブループリントのアクセス許可、管理者の同意、および資格情報の構成を確認します。
HTTP 401 FileAccessException Microsoft Graph が、ファイルの取得に使用されたトークンを拒否しました。 トークンを更新または修正し、選択したアクターを確認します。
HTTP 403 FileAccessException ID に、要求されたアイテムに対する同意またはアクセス権がありません。 同意、共有、ブループリントのアクセス許可、アイテム アクセスを確認します。
該当なし FileUrlExpiredException ボットの事前承認されたファイル URL の有効期限が切れてから、取得または再読み込みができません。 ユーザーにファイルを再度添付するように依頼します。 一度ダウンロードして再利用 DownloadedFile。
該当なし FileScopeNotSupportedException 上位レベルのファイル API が、サポートされていないカンバセーション スコープを受け取りました。 個人用チャットを使用するか、Microsoft Graph 経由で保存されているファイルを取得します。
該当なし FileException 既知の SDK ファイル操作が別の理由で失敗しました。 一般的なユーザー メッセージを示し、サニタイズされた診断をログに記録します。
HTTP 5xx トランスポートまたはサービス エラー Microsoft Graph またはストレージ サービスは要求を完了できませんでした。 サービス ガイダンスに従って再試行し、元の例外を保持します。

403 は、同意が見つからない、項目アクセスが見つからない、または存在しない項目を表す場合があります。 SDK では、これらの応答に対して個別の "ファイルが見つかりません" エラーは公開されません。

try
{
    DownloadedFile downloaded =
        await file.DownloadAsync(cancellationToken);
}
catch (FileCredentialException)
{
    await context.ReplyAsync(
        "The agent isn't configured to access this file.",
        cancellationToken);
}
catch (FileAccessException error)
{
    await context.ReplyAsync(
        $"The storage service denied access ({error.Status}).",
        cancellationToken);
}
catch (FileUrlExpiredException)
{
    await context.ReplyAsync(
        "That file link expired. Attach the file again.",
        cancellationToken);
}
catch (FileScopeNotSupportedException)
{
    await context.ReplyAsync(
        "File download isn't supported in this conversation.",
        cancellationToken);
}
catch (FileException)
{
    await context.ReplyAsync(
        "The file couldn't be read.",
        cancellationToken);
}

キーの型と値:

  • FileCredentialException: 不足しているエージェント ユーザーの Graph 資格情報構成を処理します。
  • FileAccessException.Status: 拒否されたトークンと拒否されたアクセスを区別します。
  • FileUrlExpiredException: 期限切れのボット ダウンロード URL を処理します。
  • FileScopeNotSupportedException: サポートされていないグループまたはチャネルの取得を処理します。
  • FileException: 既知の SDK エラーのフォールバック処理を提供します。
状態コード エラー コード 説明 開発者の処置
該当なし FileCredentialError エージェント ユーザー ファイルの取得に使用できる Graph 資格情報はありません。 ブループリントのアクセス許可、管理者の同意、および資格情報の構成を確認します。
HTTP 401 または 403 FileAccessError Microsoft Graph がトークンを拒否したか、アイテム アクセスを拒否しました。 statusを検査し、トークン、同意、共有、およびアイテム アクセスを確認します。
該当なし FileUrlExpiredError ボットの事前承認されたファイル URL の有効期限が切れてから、取得または再読み込みができません。 ユーザーにファイルを再度添付するように依頼します。 一度ダウンロードして、ダウンロードしたファイルを再利用します。
該当なし FileScopeNotSupportedError 上位レベル API が、サポートされていない会話スコープを受け取りました。 個人用チャットを使用するか、Microsoft Graph 経由で保存されているファイルを取得します。
該当なし FileError 既知の SDK ファイル操作が別の理由で失敗しました。 一般的なユーザー メッセージを示し、サニタイズされた診断をログに記録します。
HTTP 5xx トランスポートまたはサービス エラー Microsoft Graph またはストレージは要求を完了できませんでした。 サービス ガイダンスに従って再試行し、元のエラーを保持します。
try {
  const downloaded = await file.download();
} catch (error) {
  if (error instanceof FileCredentialError) {
    await send("The agent isn't configured to access this file.");
  } else if (error instanceof FileAccessError) {
    await send(`The storage service denied access (${error.status}).`);
  } else if (error instanceof FileUrlExpiredError) {
    await send('That file link expired. Attach the file again.');
  } else if (error instanceof FileScopeNotSupportedError) {
    await send("File download isn't supported in this conversation.");
  } else if (error instanceof FileError) {
    await send("The file couldn't be read.");
  } else {
    throw error;
  }
}

キーの型と値:

  • FileCredentialError: 不足している agentic-user Graph 資格情報を処理します。
  • FileAccessError.status: 拒否されたトークンと拒否されたアクセスを区別します。
  • FileUrlExpiredError: 期限切れのボット ダウンロード URL を処理します。
  • FileScopeNotSupportedError: サポートされていない会話スコープを処理します。
  • FileError: 既知の SDK エラーのフォールバック処理を提供します。
状態コード エラー コード 説明 開発者の処置
該当なし FileCredentialError エージェント ユーザー ファイルの取得に使用できる Graph 資格情報はありません。 ブループリントのアクセス許可、管理者の同意、および資格情報の構成を確認します。
HTTP 401 または 403 FileAccessError Microsoft Graph がトークンを拒否したか、アイテム アクセスを拒否しました。 statusを検査し、トークン、同意、共有、およびアイテム アクセスを確認します。
該当なし FileUrlExpiredError ボットの事前承認されたファイル URL の有効期限が切れてから、取得または再読み込みができません。 ユーザーにファイルを再度添付するように依頼します。 一度ダウンロードして、ダウンロードしたファイルを再利用します。
該当なし FileScopeNotSupportedError 上位レベル API が、サポートされていない会話スコープを受け取りました。 個人用チャットを使用するか、Microsoft Graph 経由で保存されているファイルを取得します。
該当なし FileError 既知の SDK ファイル操作が別の理由で失敗しました。 一般的なユーザー メッセージを示し、サニタイズされた診断をログに記録します。
HTTP 5xx トランスポートまたはサービス エラー Microsoft Graph またはストレージは要求を完了できませんでした。 サービス ガイダンスに従って再試行し、元のエラーを保持します。
try:
    downloaded = await file.download()
except FileCredentialError:
    await ctx.reply("The agent isn't configured to access this file.")
except FileAccessError as error:
    await ctx.reply(
        f"The storage service denied access ({error.status})."
    )
except FileUrlExpiredError:
    await ctx.reply("That file link expired. Attach the file again.")
except FileScopeNotSupportedError:
    await ctx.reply(
        "File download isn't supported in this conversation."
    )
except FileError:
    await ctx.reply("The file couldn't be read.")

キーの型と値:

  • FileCredentialError: 不足している agentic-user Graph 資格情報を処理します。
  • FileAccessError.status: 拒否されたトークンと拒否されたアクセスを区別します。
  • FileUrlExpiredError: 期限切れのボット ダウンロード URL を処理します。
  • FileScopeNotSupportedError: サポートされていない会話スコープを処理します。
  • FileError: 既知の SDK エラーのフォールバック処理を提供します。

設計ガイドラインとベスト プラクティス

これらのガイドラインを適用して、安全で予測可能なファイル エクスペリエンスを作成します。 ハンドラーを設計するとき、およびアプリをデプロイする前に、これらを確認します。

  • エージェントがファイルを受信したか、またはファイルのアップロードを求めているかをユーザーに伝えます。
  • ファイルの受信とアップロードが成功したことを確認します。
  • ボットがドキュメントを送信する前にファイルの同意を要求する理由を説明します。
  • ユーザーに繰り返しプロンプトを表示することなく、拒否された同意を処理します。
  • ドキュメントの添付ファイルを手動で解析するよりも、Teams SDK ファイル アクセサーを優先します。
  • 一度ダウンロードして、コンテンツを繰り返し処理する場合にダウンロードしたファイルを再利用してください。
  • 完全なファイルをメモリにバッファリングする代わりに、大きなファイルを Stream します。
  • アップローダーが提供するファイル名をサニタイズし、アプリケーションが制御する宛先を使用します。
  • 拡張子や報告された MIME の種類を信頼するのではなく、実際のコンテンツを検証します。
  • ファイルのバイト数、資格情報、識別子、または事前承認されたアップロードとダウンロードの URL をログに記録しないでください。
  • 保留中のファイル同意アップロードに有効期限ポリシーとクリーンアップ ポリシーを適用します。
  • キャンセルを伝達し、HTTP、資格情報、スコープ、有効期限のエラーを処理します。

関連項目

これらのリソースを使用して、関連する Teams SDK ファイル API、インライン画像、認証、およびメッセージングのガイダンスを確認します。