Azure OpenAI 「DALL-E」APIで作成した画像をOneDrive for Businessに保存する方法

# はじめに
先日Qiitaで、[Azure OpenAIで画像生成AI「DALL-E」のAPIを利用して、Power Appsで画像生成を楽しむ方法](https://qiita.com/DEmodoriGatsuO/items/a57779789de38add4b22)をご紹介しました。

https://qiita.com/DEmodoriGatsuO/items/a57779789de38add4b22

この方法で取得できるデータは、`画像の参照先URL`になります。

https://learn.microsoft.com/ja-jp/training/modules/generate-images-azure-openai/1-introduction

**一時的に画像が保存されているURLであり、永続的な保管場所ではありません。**


:::note warn
筆者が試したところ、数日も保管されていないURLではないかと

参考までに先日生成した画像URLはアクセスできない状態です
`https://dalleproduse.blob.core.windows.net/private/images/77b3aa08-d447-4dd8-945d-c1493fb3a920/generated_00.png?se=2023-09-19T14%3A32%3A22Z&sig=ekgMUUKw9JNWB1yIHID3pqlJJahMIkAd4VQcZeZv07g%3D&ske=2023-09-25T09%3A13%3A59Z&skoid=09ba021e-c417-441c-b203-c81e5dcd7b7f&sks=b&skt=2023-09-18T09%3A13%3A59Z&sktid=33e01921-4d64-4f8c-a055-5bdaffd5e33d&skv=2020-10-02&sp=r&spr=https&sr=b&sv=2020-10-02`


:::

**せっかく作成したデータなので、画像データを保存したい。**
そんなニーズもあると思います。

やり方を模索していましたが、Power Automateで活用できる
**OneDrive for Business**の[URL からファイルをアップロード](https://learn.microsoft.com/ja-jp/connectors/onedriveforbusiness/#url-%E3%81%8B%E3%82%89%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E3%82%A2%E3%83%83%E3%83%97%E3%83%AD%E3%83%BC%E3%83%89)アクションで、
非常に簡単に実現できたので、今回はその紹介をいたします。

https://learn.microsoft.com/ja-jp/connectors/onedriveforbusiness/#url-%E3%81%8B%E3%82%89%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E3%82%A2%E3%83%83%E3%83%97%E3%83%AD%E3%83%BC%E3%83%89

前回の復習と同時に見ていきましょう。

## 前回のフローのおさらい

 - 全体像
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/6caf75f2-d673-c6a6-5e17-59490f9c621d.png)

 - Do untilの中身
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/e8fc0d2c-d1c4-f245-6de0-6c189b60b43a.png)


1. Azure OpenAIのAPIを実行、画像生成用のURLを取得
1. URLに対し、Get Requestを送信、生成が完了するまでLoop
1. 画像生成が完了次第、画像参照先のURLが発行される

上記のようなフローになります。

このフローで生成された**画像参照先のURL**が、
**OneDrive for Business**の[URL からファイルをアップロード](https://learn.microsoft.com/ja-jp/connectors/onedriveforbusiness/#url-%E3%81%8B%E3%82%89%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E3%82%A2%E3%83%83%E3%83%97%E3%83%AD%E3%83%BC%E3%83%89)アクションに使用する
**パラメーター**になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/23567826-6e3f-1907-5432-ec370146045c.png)


# **OneDrive for Business**の[URL からファイルをアップロード](https://learn.microsoft.com/ja-jp/connectors/onedriveforbusiness/#url-%E3%81%8B%E3%82%89%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E3%82%A2%E3%83%83%E3%83%97%E3%83%AD%E3%83%BC%E3%83%89)アクション

公式 Referenceを見ていきましょう。

https://learn.microsoft.com/ja-jp/connectors/onedriveforbusiness/#url-%E3%81%8B%E3%82%89%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E3%82%A2%E3%83%83%E3%83%97%E3%83%AD%E3%83%BC%E3%83%89


> URL からファイルをアップロード アクションは、実際の結果に関係なく、常に 20 秒後に成功を報告します。 ファイルサイズやソースのダウンロード速度によっては、アップロード プロセスに時間がかかる場合があります。 実際の結果を確認するには、アップロードされたファイルデータを操作する前に、常にフローにロジックを作成して、ファイルの存在やタイムアウトを確認する必要があります。
>> 公式より

大雑把な表現になりますが、**ソースURL**から上手い具合にデータを取得してくれる
便利なアクションです。

## このアクションのポイント
**パラメーター**を公式より引用します。
|Name|キー|必須|型|説明
|---|---|---|---|---
|ソース URL|source|True|string|ソースファイルへのURL。
|宛先ファイル パス|destination|True|string|ターゲット ファイル名を含む、宛先ファイル パス。|String
|上書き|overwrite||boolean|[true] に設定されている場合、宛先ファイルを上書きします。

 - **ソース URL**は、DALL-EのAPIで取得した画像参照先です
 - **宛先ファイル パス**は、直入力でファイル名も含めて、指定します。
     - Exploreでフォルダの場所を設定できないため、書き方に注意が必要です。 
     - ex) `/Download/filename.png`

**非常に便利なアクションです。**
ファイル名も動的な変数に設定することで、複数のファイルを保存することができます。

## このアクションを活用できていなかったときの私の場合
方法がわからなかった私は、**「PythonでRequestを送り、ファイルを作成する」**
という遠回りをしてしまいました・・・。


```python:sample.py
import requests
import os

# get requestを送り、ファイルを書き出す方法
def save_image_from_url(url, save_path):
    response = requests.get(url, stream=True)
    response.raise_for_status()
    with open(save_path, 'wb') as file:
        for chunk in response.iter_content(chunk_size=8192):
            file.write(chunk)

filename = "FileName.png"
save_path = os.path.join("FolderName", filename)
save_image_from_url(`DALL-Eで取得したソース URL`, save_path)
print(f"Image saved to {save_path}")
```

ChatGPT先生に教えていただいた方法です。
コード解決は簡単ですが、Power Automateに既に機能があるのであれば、
そちらを活用するほうが**ベター**でしょう。

# 全体像を見てみる
さて、「**インスタント クラウド フロー**」で一回試してみます。
Promptは、「A dog of photo」です。

- フロー全体
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/0967aa34-12e9-3d6a-5081-ee7a8d2f77f1.png)

- Do until
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/66cf3c2c-5c43-9ece-422e-cf956bd41df4.png)

- Apply to each
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/f314c685-ddce-6f41-9479-a69e7bc546dd.png)

## 前回との差分
`JSON の解析`アクションを追加しています。
やはりちゃんとやった方が良いですね💦

まずは`DALL`のAPIを起動するアクションから、**ヘッダー**の値を取得しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/4edc9eb6-7d2a-5ff9-b04f-3f7bb0c4c286.png)

:::note
**ヘッダーであることがPoint !!** 本文ではありません！
:::

```json:スキーマ
{
    "type": "object",
    "properties": {
        "operation-location": {
            "type": "string"
        },
        "x-ms-request-id": {
            "type": "string"
        },
        "x-envoy-upstream-service-time": {
            "type": "string"
        },
        "OpenAI-Processing-Ms": {
            "type": "string"
        },
        "x-ms-client-request-id": {
            "type": "string"
        },
        "apim-request-id": {
            "type": "string"
        },
        "Strict-Transport-Security": {
            "type": "string"
        },
        "X-Content-Type-Options": {
            "type": "string"
        },
        "x-ms-region": {
            "type": "string"
        },
        "Date": {
            "type": "string"
        },
        "Content-Length": {
            "type": "string"
        },
        "Content-Type": {
            "type": "string"
        }
    }
}
```

そして取得した`operation-location`に`GET` Requestを送信します。
画像が生成されるまで、スキーマの形は厳密には異なるのですが、下記のスキーマで問題なく進めることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/6bacf531-91e6-4d19-a2a0-f46d61c964f5.png)

```json:スキーマ
{
    "type": "object",
    "properties": {
        "created": {
            "type": "integer"
        },
        "expires": {
            "type": "integer"
        },
        "id": {
            "type": "string"
        },
        "result": {
            "type": "object",
            "properties": {
                "created": {
                    "type": "integer"
                },
                "data": {
                    "type": "array",
                    "items": {
                        "type": "object",
                        "properties": {
                            "url": {
                                "type": "string"
                            }
                        },
                        "required": [
                            "url"
                        ]
                    }
                }
            }
        },
        "status": {
            "type": "string"
        }
    }
}
```

このAPIは複数の画像生成が出来る関係もあるのか、
`画像参照先 URL`である`data`は**array**です・・・。

インデックスで一つだけ取得する方法もありますが、
素直に`Apply to each`を採用しましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/2caea371-9a58-c525-2f9a-8d205c09bd4d.png)

:::note warn
ファイル名（正しくは ファイルのパス）が重複した場合に、
エラーもしくは設定に基づき上書きされる可能性があるので、一意の名前になるように
気を付けましょう。
:::

## Output

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/d0dc79b9-b0d1-1bf9-750f-deac475ad4eb.png)


可愛いワンちゃんの写真が`One Drive for Business`に保管されました。
**改めてとても簡単ですね。** 本物かと疑ってしまいます・・・💦

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/ca4b5912-4ceb-5e6d-bddf-0f70a233046e.png)

# フローを見返す
改めて、**とてもフローが長くなっていることがわかります。**
長いコードはメンテナンスが大変になります。

処理のまとまりがある場合、
 - RPAではサブフロー
 - プログラミングであれば関数
上記のような解決ができます。

Power Automate上で実施する方法は**子フロー**の活用です。

今回の記事は長くなってしまいましたので、次回の記事て
**ソリューションと子フロー**について、見ていきたいと思います。

**有益な記事にするつもりですので、楽しみにしていてください。**

# 追伸
先日、**GPT(gpt-35-turbo 0613) とPaLM 2(chat-bison-32k)でしりとりをする**という実験をしました。

<iframe width="560" height="315" src="https://www.youtube.com/embed/6zaZ_flWsoE?si=BQS5O7xj9Q7s25c8" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

非常に不毛な実験ですね。懺悔です。

https://note.com/bonito_vba_gas/n/n0df092c0a40f

# Reference
- [Azure OpenAI Service を使用して画像を生成する](https://learn.microsoft.com/ja-jp/training/modules/generate-images-azure-openai/)