1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

blastengine APIで添付ファイル付きメール配信を実装する

1
Posted at

はじめに

メール配信APIを使っていると、PDFの請求書や画像ファイルを添付して送りたい場面が出てきます。blastengine APIでは、トランザクション配信で添付ファイル付きメールを送信できます。

本記事では、blastengine APIを使った添付ファイル付きメール配信の実装方法を解説します。サイズ制限や添付不可拡張子など、実装時にハマりやすいポイントも押さえていきます。

添付ファイルの制約

実装に入る前に、blastengine APIの添付ファイルに関する制約を整理しておきます。

サイズ制限

項目 制限
1配信あたりの合計サイズ デフォルト1MB(契約により異なる)
添付ファイル数の上限 100件

制限内であれば複数ファイルを添付可能です。

添付不可の拡張子

セキュリティ上の理由から、以下の拡張子は添付できません。

.ade, .adp, .apk, .appx, .appxbundle, .bat, .bz2, .cab, .chm, .cmd,
.com, .cpl, .dll, .dmg, .ex, .ex_, .exe, .gz, .hta, .ins, .isp, .iso,
.jar, .js, .jse, .lib, .lnk, .mde, .msc, .msi, .msix, .msixbundle,
.msp, .mst, .nsh, .pif, .ps1, .scr, .sct, .shb, .sys, .tgz, .vb,
.vbe, .vbs, .vxd, .wsc, .wsf, .wsh, .zip

実行可能ファイルや圧縮ファイルが中心です。PDF・画像・Excelなどの一般的なビジネスファイルは問題なく添付できます。

環境準備

BearerTokenの生成

blastengine APIの認証にはBearerTokenが必要です。ログインIDとAPIキーから生成します。

YOUR_LOGIN_ID="your_login_id"
YOUR_API_KEY="your_api_key"

YOUR_BEARER_TOKEN=$(echo -n "${YOUR_LOGIN_ID}${YOUR_API_KEY}" \
  | shasum -a 256 | awk '{print $1}')
YOUR_BEARER_TOKEN=$(echo -n "${YOUR_BEARER_TOKEN}" | tr A-Z a-z)
YOUR_BEARER_TOKEN=$(echo -n "${YOUR_BEARER_TOKEN}" | base64 | tr -d "\n")

echo "${YOUR_BEARER_TOKEN}"

共通設定(Python)

本記事ではPythonで実装します。requestsライブラリを使用します。

pip install requests
import requests
import os

API_BASE = "https://app.engn.jp/api/v1"
BEARER_TOKEN = "your_bearer_token"

HEADERS_JSON = {
    "Authorization": f"Bearer {BEARER_TOKEN}",
    "Content-Type": "application/json",
}

HEADERS_MULTIPART = {
    "Authorization": f"Bearer {BEARER_TOKEN}",
    # Content-Typeはrequestsが自動設定するため指定しない
}

実装1:添付ファイル付きトランザクション配信(curl)

まずはcurlで基本的な動作を確認します。添付ファイル付きの場合、multipart/form-data形式でリクエストします。

curl -X POST "https://app.engn.jp/api/v1/deliveries/transaction" \
  -H "Authorization: Bearer ${YOUR_BEARER_TOKEN}" \
  -F 'data={"from":{"email":"sender@example.com","name":"送信者名"},"to":"recipient@example.jp","subject":"請求書送付のご案内","text_part":"請求書を添付いたします。ご確認ください。"};type=application/json' \
  -F "file=@/path/to/invoice.pdf"

ポイントは、JSONパラメータをdataパートにまとめ、添付ファイルをfileパートで送ることです。通常のJSON送信とは異なり、multipart/form-dataとして送信します。

実装2:Pythonで添付ファイル付き配信

単一ファイル添付

import json

def send_with_attachment(to_email, subject, body, file_path):
    """添付ファイル付きトランザクション配信"""
    url = f"{API_BASE}/deliveries/transaction"

    payload = {
        "from": {"email": "sender@example.com", "name": "送信者名"},
        "to": to_email,
        "subject": subject,
        "text_part": body,
    }

    file_name = os.path.basename(file_path)
    with open(file_path, "rb") as f:
        files = {
            "data": (None, json.dumps(payload), "application/json"),
            "file": (file_name, f, "application/octet-stream"),
        }
        response = requests.post(
            url, headers=HEADERS_MULTIPART, files=files
        )

    if response.status_code == 201:
        delivery_id = response.json()["delivery_id"]
        print(f"配信成功: delivery_id={delivery_id}")
        return delivery_id
    else:
        print(f"配信失敗: {response.status_code} {response.text}")
        return None


# 実行例
send_with_attachment(
    to_email="recipient@example.jp",
    subject="請求書送付のご案内",
    body="請求書を添付いたします。ご確認ください。",
    file_path="/path/to/invoice.pdf",
)

filesパラメータにdatafileを渡すのがポイントです。dataパートにはJSONペイロードをapplication/jsonとして格納し、fileパートに添付ファイルを入れます。

複数ファイル添付

複数ファイルを添付する場合、filesパラメータにタプルのリストを渡します。

def send_with_multiple_attachments(to_email, subject, body, file_paths):
    """複数添付ファイル付きトランザクション配信"""
    url = f"{API_BASE}/deliveries/transaction"

    payload = {
        "from": {"email": "sender@example.com", "name": "送信者名"},
        "to": to_email,
        "subject": subject,
        "text_part": body,
    }

    # dataパートにJSON、fileパートに複数ファイルを送信
    opened_files = []
    file_tuples = [("data", (None, json.dumps(payload), "application/json"))]
    try:
        for file_path in file_paths:
            f = open(file_path, "rb")
            opened_files.append(f)
            file_name = os.path.basename(file_path)
            file_tuples.append(("file", (file_name, f, "application/octet-stream")))

        response = requests.post(
            url, headers=HEADERS_MULTIPART, files=file_tuples
        )
    finally:
        for f in opened_files:
            f.close()

    if response.status_code == 201:
        delivery_id = response.json()["delivery_id"]
        print(f"配信成功: delivery_id={delivery_id}")
        return delivery_id
    else:
        print(f"配信失敗: {response.status_code} {response.text}")
        return None


# 実行例
send_with_multiple_attachments(
    to_email="recipient@example.jp",
    subject="月次レポート送付",
    body="レポートと集計データを添付いたします。",
    file_paths=[
        "/path/to/report.pdf",
        "/path/to/data.xlsx",
    ],
)

実装3:添付前のバリデーション

配信エラーを未然に防ぐため、送信前にバリデーションを入れておくと安心です。

BLOCKED_EXTENSIONS = {
    ".ade", ".adp", ".apk", ".appx", ".appxbundle", ".bat", ".bz2",
    ".cab", ".chm", ".cmd", ".com", ".cpl", ".dll", ".dmg", ".ex",
    ".ex_", ".exe", ".gz", ".hta", ".ins", ".isp", ".iso", ".jar",
    ".js", ".jse", ".lib", ".lnk", ".mde", ".msc", ".msi", ".msix",
    ".msixbundle", ".msp", ".mst", ".nsh", ".pif", ".ps1", ".scr",
    ".sct", ".shb", ".sys", ".tgz", ".vb", ".vbe", ".vbs", ".vxd",
    ".wsc", ".wsf", ".wsh", ".zip",
}

MAX_TOTAL_SIZE = 1 * 1024 * 1024  # 1MB(デフォルト)
MAX_FILE_COUNT = 100


def validate_attachments(file_paths, max_size=MAX_TOTAL_SIZE):
    """添付ファイルのバリデーション"""
    errors = []

    if len(file_paths) > MAX_FILE_COUNT:
        errors.append(f"添付ファイル数が上限({MAX_FILE_COUNT}件)を超えています")

    total_size = 0
    for file_path in file_paths:
        # 拡張子チェック
        _, ext = os.path.splitext(file_path)
        if ext.lower() in BLOCKED_EXTENSIONS:
            errors.append(f"添付不可の拡張子です: {file_path}")

        # ファイル存在チェック
        if not os.path.exists(file_path):
            errors.append(f"ファイルが存在しません: {file_path}")
            continue

        total_size += os.path.getsize(file_path)

    # 合計サイズチェック
    if total_size > max_size:
        size_mb = total_size / (1024 * 1024)
        limit_mb = max_size / (1024 * 1024)
        errors.append(
            f"合計サイズ({size_mb:.2f}MB)が上限({limit_mb:.2f}MB)を超えています"
        )

    return errors


# バリデーション付き送信
def send_with_validation(to_email, subject, body, file_paths):
    """バリデーション付き添付ファイル配信"""
    errors = validate_attachments(file_paths)
    if errors:
        for e in errors:
            print(f"エラー: {e}")
        return None

    return send_with_multiple_attachments(to_email, subject, body, file_paths)

実装4:配信結果の確認

送信後、配信が正常に完了したかを配信詳細APIで確認します。

def get_delivery_detail(delivery_id):
    """配信詳細を取得"""
    url = f"{API_BASE}/deliveries/{delivery_id}"
    response = requests.get(url, headers=HEADERS_JSON)

    if response.status_code == 200:
        data = response.json()
        print(f"配信ID: {data['delivery_id']}")
        print(f"ステータス: {data['status']}")
        print(f"配信種別: {data['delivery_type']}")
        print(f"件名: {data['subject']}")
        if "attaches" in data:
            print(f"添付ファイル数: {len(data['attaches'])}")
            for attach in data["attaches"]:
                print(f"  - {attach}")
        return data
    else:
        print(f"取得失敗: {response.status_code}")
        return None

実装時の注意点まとめ

ポイント 詳細
Content-Type 添付ありはmultipart/form-data、なしはapplication/json
サイズ上限 デフォルト1MB(契約で変更可能)
ファイル数上限 100件/配信
拡張子制限 実行可能ファイル・圧縮ファイルは不可
送信前バリデーション API呼び出し前にローカルでチェックするとエラーを減らせる

おわりに

blastengine APIの添付ファイル付き配信は、multipart/form-data形式でリクエストする点がJSON送信との違いです。サイズ制限と拡張子制限を事前にバリデーションしておけば、実運用でもスムーズに使えます。

請求書や帳票の自動送信など、業務自動化の一部としてぜひ活用してみてください。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?