search
LoginSignup
11

More than 3 years have passed since last update.

posted at

updated at

react-dropzone、Express、Firebaseを使った画像のアップロード

components/FileUpload.ts
const onDrop = (acceptedFiles: File[]) => {
     console.log(formData)
 }

return (
     <div>
         <DropZone  onDrop={onDrop}>
             {({getRootProps, isDragActive}) => (
                 <div {...getRootProps()}>
                     {isDragActive ? "Drop it like it's hot!" : 'Drag a file to upload!'}
                 </div>
             )}
         </DropZone>
     </div>
 )

react-dropzoneを使ってこんな感じに書いて、描画された部分に画像をドロップしてみると

lastModified: 1571463132902
lastModifiedDate: Sat Oct 19 2019 14:32:12 GMT+0900 (日本標準時) {}
name: "hoge.PNG"
path: "hoge.PNG"
size: 353348
type: "image/png"
webkitRelativePath: ""

こんな感じのFileっていう型の配列がもらえる。
んじゃあこのデータ配列そのままサーバ側にぶん投げて、サーバ側でfirebase storageにpath指定してあげりゃあ画像のアップロード実装できんじゃん!
って思ってたんだけど、見てわかる通りpathがfullpath(?)になってない。どうしよう...

最終的に、サーバ側に一時的に画像を保存して、そこで生成されたpathをfirebase側に指定してあげたらうまく行った。

そのために、まずはreact-dropzoneから受け取ったFile配列をFormDataとやらに変換して、それをPOSTデータとしてぶん投げた。

components/FileUpload.ts
 const onDrop = (acceptedFiles: File[]) => {
     const formData = new FormData();
     acceptedFiles.forEach(file => {
         formData.append('Files', file)
     })
     dispatch(postFiles.request(formData))
 }

参考にさせていただいたサイト様様 → https://qiita.com/uryyyyyyy/items/9954205a620b6f3c1f24

んで、これをExpress側で受け取るんだけど、このデータはmultipart/form-dataというものらしく、普通にやったらうまく受け取れなかった。なんやそれ。知らんがな。

app.ts

import multer from 'multer';

const upload = multer({ dest: './uploads/' });

app.post('/files', upload.fields([ { name: 'Files' } ]), postStorageController);
controller/postStorageController.ts

export function postStorageController(req: Request, res: Response, next: NextFunction) {
    const files: Express.Multer.File[] = req.files['Files'];

こういう風に書いてあげると、filesの部分で以下のような感じで値がもらえる

[Object: null prototype] {
  Files:
   [ { fieldname: 'Files',
       originalname: 'hoge.PNG',
       encoding: '7bit',
       mimetype: 'image/png',
       destination: 'uploads/',
       filename: 'd18eb9674f13a81898e7c25bb0c3bda6',
       path: 'uploads/d18eb9674f13a81898e7c25bb0c3bda6',
       size: 353348 } ] }

参考にさせていただいたサイト様様 → https://qiita.com/uryyyyyyy/items/9954205a620b6f3c1f24
(さっきと同じ記事。圧倒的感謝)

あとは、Google先生の解説を見ながらfirebase storageにあげれば終わり!

controller/postStorageController.ts
export function postStorageController(req: Request, res: Response, next: NextFunction) {
    const files: Express.Multer.File[] = req.files['Files'];

    files.forEach(fileInfo => {
        const uploadFilePath = `files/${fileInfo.originalname}`
        bucket.upload(fileInfo.path, { destination: uploadFilePath, contentType: fileInfo.mimetype }, error => {
            if (error) {
                console.log(`failed storage post ${fileInfo.originalname}`);
                console.log(error.message);
                return res.sendStatus(404);
            } else {
                console.log(`success storage post ${fileInfo.originalname}`);
                return res.sendStatus(200);
            }
    }
}

↓↓↓今回書いたソースコード

(フロント)
https://github.com/ShotaroOkada/fileupload_react_client

(サーバ)
https://github.com/ShotaroOkada/fileupload_express_api

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
What you can do with signing up
11