はじめに
がちもとさんアドベントカレンダー21日目の記事です。
Qiitaは、毎月100MBまでという画像アップロード制限があります。
ひとりアドベントカレンダー諦めそう @Qiita pic.twitter.com/1bDouadIr3
— がちもとさん (@sotongshi) December 20, 2023
アドベントカレンダーを諦めかけたので、スクショを取得し、S3にアップロード、マークダウン形式のリンクを発行するやつを作りました。
開発環境
- Windows 11 PC
- Vue3
- AWS Lambda(Python 3.12)
導入
1.画像をS3にアップロードするAPI(Lambda関数)を作成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
lambda_function.py import json import boto3 import base64 import uuid from datetime import datetime import os # 環境変数からAPIキーを取得 api_key = os.environ.get('API_KEY') # AWSリソースのセットアップ s3 = boto3.client('s3') def lambda_handler(event, context): try: # リクエストからAPIキーを取得 provided_api_key = event['headers']['x-api-key'] print(provided_api_key) # 提供されたAPIキーが設定されたAPIキーと一致するかを確認 if provided_api_key != api_key: return { 'statusCode': 401, 'body': json.dumps({'message': 'APIキーが無効です。'}) } # イベントからデータを取得 event_body = json.loads(event['body']) # JSON文字列をPython辞書に変換 image_data_base64 = event_body.get('imageData') bucket_name = 'gachimoto-qiita-storage' # 日付とUUIDを使用してファイル名を生成 timestamp = datetime.now().strftime('%Y%m%d-%H%M%S') unique_id = str(uuid.uuid4())[:8] # ランダムな8文字のUUID file_name = f'{timestamp}-{unique_id}.png' # S3に保存するファイル名 # Base64形式のデータをバイナリにデコード image_data_binary = base64.b64decode(image_data_base64) # S3にバイナリデータを直接アップロード s3.put_object( Bucket=bucket_name, Key=file_name, Body=image_data_binary, ContentType='image/png' # ContentTypeを指定 ) # アップロードされた画像のURLを生成 image_url = f'https://{bucket_name}.s3.amazonaws.com/{file_name}' return { 'statusCode': 200, 'body': json.dumps({'message': f"![{file_name}]({image_url})"}) } except Exception as e: return { 'statusCode': 500, 'body': json.dumps({'message': str(e)}) } |
2.Lambda関数の設定
4.S3の設定
バケットポリシー
1 2 3 4 5 6 7 8 9 10 11 12 |
{ "Version": "2012-10-17", "Statement": [ { "Sid": "PublicReadGetObject", "Effect": "Allow", "Principal": "*", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::gachimoto-qiita-storage/*" } ] } |
5.Vue3のアプリ作成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
App.vue <template> <div> <button @click="captureScreenshot">スクリーンショットを取得</button> <br> <br> <img v-if="imageURL" :src="imageURL" alt="クリップボードの画像" style="max-width: 200px; height: auto;"> <p v-if="successMessage">{{ successMessage }}</p> <p v-if="imageUrl">{{ imageUrl }}</p> <button @click="copyImageUrlToClipboard" v-if="imageUrl">画像リンクをコピー</button> </div> </template> <script> export default { data() { return { imageURL: null, successMessage: '', imageUrl: '', }; }, methods: { async captureScreenshot() { try { const clipboardItems = await navigator.clipboard.read(); for (const clipboardItem of clipboardItems) { if (clipboardItem.types.includes('image/png')) { const blob = await clipboardItem.getType('image/png'); const reader = new FileReader(); reader.onloadend = () => { const image_data_base64 = reader.result.split(',')[1]; this.imageURL = 'data:image/png;base64,' + image_data_base64; // Lambda関数のエンドポイントURLを設定 const lambda_endpoint_url = 'https://xxxx.lambda-url.ap-northeast-1.on.aws/'; // Lambda関数に渡すデータ const lambda_data = { 'imageData': image_data_base64, }; const apiKey = process.env.VUE_APP_API_KEY; // Lambda関数にHTTP POSTリクエストを送信 fetch(lambda_endpoint_url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey }, body: JSON.stringify(lambda_data), }) .then((response) => { if (response.status === 200) { return response.json(); } else { throw new Error(`HTTPリクエストエラー: ステータスコード ${response.status}`); } }) .then((responseData) => { console.log('Lambdaからのレスポンス:', responseData); this.successMessage = '画像がアップロードされました。'; this.imageUrl = responseData.message; }) .catch((error) => { console.error('エラー:', error); this.successMessage = '画像のアップロードに失敗しました。'; }); }; reader.readAsDataURL(blob); } } } catch (error) { console.error('クリップボードからの画像取得エラー', error); this.successMessage = '画像のアップロードに失敗しました。'; } }, copyImageUrlToClipboard() { // imageUrlをクリップボードにコピーする処理を実装 const textArea = document.createElement("textarea"); textArea.value = this.imageUrl; document.body.appendChild(textArea); textArea.select(); document.execCommand('copy'); document.body.removeChild(textArea); }, }, }; </script> |
1 2 3 |
.env VUE_APP_API_KEY=xxxx |
実行結果
お疲れさまでした。