Quantcast
Channel: Node.jsタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9021

【React】ドラッグアンドドロップでS3に画像アップしたい

$
0
0
ドラッグアンドドロップでフロントエンド(React)からサーバーサイド(Node.js)に リクエストを送ってS3に画像を保存するやり方を纏めました。 やること フロントエンドで画像データを取得、サーバーサイドで受け取った画像データをS3に保存する ※リクエストを投げる、受け取る辺りの処理は省いています 使用ライブラリ react-dropzone AWS SDK for JavaScript フロントエンドで画像データを取得する 画像をドロップアンドドラッグする領域を作成 App.tsx import "./styles.css"; export default function App() { return ( <div className="drop-area"> <h1>画像をドラッグ</h1> </div> ); } styles.css .drop-area{ color: gray; font-weight: bold; font-size: 1.2em; display: flex; justify-content: center; align-items: center; width: 400px; height: 250px; border: 5px solid gray; border-radius: 15px; margin-bottom: 20px; } img{ height: 240px; } react-dropzoneでドラッグアンドドロップできるようにする 1.react-dropzoneをインストールしuseDropzoneをインポートする 2.画像アップで発火するonDropイベントを作成する。ここではアップされた画像の情報をログに出してみます。 3.ドラッグアンドドロップで使用するgetRootPropsとボタンでファイル選択をするgetInputPropsをuseDropzoneから取得 4.ドロップする領域のタグの属性に{...getRootProps()}を追加する事でドラッグアンドドロップしてファイル情報を取得する 5.選択して画像をアップするボタンを作成し属性に{...getRootProps()}を設定しタグ内にinputタグを設置。 inputタグにの属性には{...getInputProps()}を設定する 6.画像がアップされている状態かを判別するフラグを用意しアップされていれば画像を表示する(ここでは一旦空にします) App.tsx import "./styles.css"; import { useDropzone } from "react-dropzone"; import { useCallback, useState } from "react"; export default function App() { const [isUpload, setIsUpload] = useState(false); const onDrop = useCallback((acceptedFiles) => { const file = acceptedFiles[0]; setIsUpload(true); console.log(file); }, []); const { getRootProps, getInputProps } = useDropzone({ onDrop }); return ( <div> <div className="drop-area" {...getRootProps()}> {isUpload ? <img src="" /> : <h1>画像をドラッグ</h1>} </div> <button {...getRootProps()}> 画像を選択 <input {...getInputProps()} /> </button> </div> ); } ここまでで画像の情報が取得できました ドラッグした画像をbase64に変換する 取得したファイルの内容にアクセスする為に、FileReaderAPIを使用する必要があります。 FileReaderAPIからbase64にエンコードして画像を読み込みます App.tsx const encodeToBase64 = async (file: File) => { const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = () => { const base64 = reader.result as string; console.log(base64) }; }; 画像をリサイズしてプレビューを表示する base64に変換した画像をそのままサーバーサイドに送ると容量が大きすぎてHTTP 413 Payload Too Largeエラーが発生するのでcanvasを使ってリサイズします リサイズしたbase64をimgタグに割り当てる変数に格納すればプレビューが表示されるはずです App.tsx const resizeUpload = (base64: string) => { const imgType = base64.substring(5, base64.indexOf(';')) const img = new Image() img.onload = () => { const canvas = document.createElement('canvas') const width = img.width * 0.5 const height = img.height * 0.5 canvas.width = width canvas.height = height const ctx = canvas.getContext('2d')! ctx.drawImage(img, 0, 0, width, height) const reSizeData = canvas.toDataURL(imgType) setImgSrc(reSizeData); console.log(reSizeData) } img.src = base64 } あとは変数imgSrcに格納した画像データをサーバに送ればフロント側の実装は完了 App.tsx import "./styles.css"; import { useDropzone } from "react-dropzone"; import { useCallback, useState } from "react"; export default function App() { const [isUpload, setIsUpload] = useState(false); const [imgSrc, setImgSrc] = useState(""); const resizeUpload = (base64: string) => { const imgType = base64.substring(5, base64.indexOf(';')) const img = new Image() img.onload = () => { const canvas = document.createElement('canvas') const width = img.width * 0.5 const height = img.height * 0.5 canvas.width = width canvas.height = height const ctx = canvas.getContext('2d')! ctx.drawImage(img, 0, 0, width, height) const reSizeData = canvas.toDataURL(imgType) setImgSrc(reSizeData); } img.src = base64 } const encodeToBase64 = async (file: File) => { const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = () => { const base64 = reader.result as string; resizeUpload(base64) console.log(reSizeData) }; }; const onDrop = useCallback((acceptedFiles) => { const file = acceptedFiles[0]; setIsUpload(true); encodeToBase64(file); }, []); const { getRootProps, getInputProps } = useDropzone({ onDrop }); return ( <div> <div className="drop-area" {...getRootProps()}> {isUpload ? <img className="image" alt="投稿画像" src={imgSrc} /> : <h1>画像をドラッグ</h1>} </div> <button {...getRootProps()}> 画像を選択 <input {...getInputProps()} /> </button> </div> ); } こんな感じの動きになるはずです このリサイズしたbase64をサーバーサイドにリクエストを投げます サーバーサイドでフロントから受け取った画像データをS3に保存する aws-sdkでプログラムからAWSにアクセス AWS SDK for JavaScriptをインストール S3にバケットを作成 フロントエンドから受け取ったbase64をデコードする AWSのアクセスキーとシークレットキーを使ってS3にアクセスする index.ts import AWS, { AWSError } from 'aws-sdk' const s3 = new AWS.S3({ accessKeyId:アクセスキー, secretAccessKey: シークレットキー }) const uploadS3 = (image: string) => { const fileData = image.replace(/^data:\w+\/\w+;base64,/, '') const decodedFile = new Buffer(fileData, 'base64') const fileExtension = createFileExtension(image) const contentType = image .toString() .slice(image.indexOf(':') + 1, image.indexOf(';')) const params = { Bucket: バケット名, Key: [`KeyName`, fileExtension].join('.'), Body: decodedFile, ContentType: contentType, ACL: 'public-read-write' } s3.putObject(params, (err: AWSError) => { if (err) { throw err } }) } 備考 ※S3に保存するparamsオブジェクトの中のKeyは一意でないといけないので複数のデータを保存するときはidなどをKeyに付与しておく これでS3のバケットのにオブジェクトが作成できたのでurlをimgタグのsrcに指定すればフロントエンドでS3の画像が読み込める。 直接URLを指定することは少ないと思うので、DBにidと拡張子を保存しておいて下記のようにURLを作成すれば動的に画像を読み込めます。 https://バケット名.amazonaws.com/KeyName${id}.${fileExtension} 参考 react-dropzone AWS SDK for JavaScript [JavaScript] 画像リサイズ&回転 JavaScriptで画像をbase64形式のURLに変換するやり方

Viewing all articles
Browse latest Browse all 9021

Trending Articles