uni farm

react、firebaseでブラウザからcloud storageに音声ファイルアップロード

reactで作ったweb画面から音声ファイルをブラウザへアップロード さらにcloud storageにファイルをアップロードする

音声ファイルの扱い方はあまり情報がないので残しておく

環境

  • node: 8.11.2
  • firebase: 5.4.1
  • react-cli: 1.1.4
  • react-dropzone: 5.0.1
  • moment: 2.22.2
  • material-ui: 3.0.3

firebaseに登録、プロジェクト作成

https://firebase.google.com/docs/web/setup?hl=ja より、firebaseの登録をして、以下のようなconfigを取得する

// Initialize Firebase
// TODO: Replace with your project's customized code snippet
var config = {
  apiKey: "<API_KEY>",
  authDomain: "<PROJECT_ID>.firebaseapp.com",
  databaseURL: "https://<DATABASE_NAME>.firebaseio.com",
  storageBucket: "<BUCKET>.appspot.com",
};

変数にしてconfig.jsを作成しておく

./firebase/config.js
export const config = {
  apiKey: "<API_KEY>",
  authDomain: "<PROJECT_ID>.firebaseapp.com",
  databaseURL: "https://<DATABASE_NAME>.firebaseio.com",
  storageBucket: "<BUCKET>.appspot.com",
}

実行コード

firebase関連

upload先はconfig.jsにあるstorageBucket データはbase64形式で保存する

./firebase/index.js
import firebase from 'firebase'
import { config } from './config'

export const firebaseApp = firebase.initializeApp(config)
export const audioStorage = firebaseApp.storage()


export const uploadAudioStorage = async (dataBase64, filename) => {
  // 参照
  let storageRef = audioStorage.ref()

  // Create a reference to 'filename'
  // 格納先を指定
  let ref = storageRef.child(filename)

  // upload base64 format
  let uploadTask = ref.putString(dataBase64, 'data_url')

  // アップロード完了イベントは取れるが、progress等のイベントはなぜか取れない
  return uploadTask.then(async (snapshot) => {
    console.log('uploaded: ', snapshot)
    return await snapshot
  })
}

reactコンポーネント

dropzoneでファイルをブラウザへアップロード uploadボタンをクリックするとcloud storageへアップロードする

./components/Upload.js
import React, { Component } from 'react'
import Dropzone from 'react-dropzone'
import { Button } from "@material-ui/core"
import moment from 'moment'

import { uploadAudioStorage } from '../firebase'
import styles from './AudioToText.css'

export default class Upload extends Component {
  constructor(props) {
    super(props)
    this.state = {
      files: [],
      duration: null.
      gcsPath: null
    }
  }

  onDrop(files, rejected) {
    if (rejected.length !== 0) {
      console.log('rejected file ', rejected)
      return
    }

    files.forEach((file) => {
      const audio = new Audio()
      audio.src = file.preview
      audio.addEventListener('loadedmetadata', () => {
        // 再生時間の取得
        console.log('duration', audio.duration)
        this.setState({
          files: files,
          duration: audio.duration
        })
      })
      // ファイルの中身を読み込む
      this.readFile(file)
    })

  }

  readFile(file) {
    const reader = new FileReader()
    reader.onload = () => {
      const fileBinary = reader.result
      console.log('read file', fileBinary)

      this.setState({
        fileBinary: fileBinary
      })
    }
    reader.onabort = () => console.log('file reading was aborted')
    reader.onerror = () => console.log('file reading has failed')

    // base64で読み込む
    reader.readAsDataURL(file)
  }

  async onUpload() {
    let state = this.state
    let data = state.fileBinary
    let fileName = moment().format('YYYYMMDD') + '_' + state.files[0].name

    let res = await uploadAudioStorage(data, fileName)

    let path = 'gs://' + res.matadata.bucket + res.metadata.fullPath

    this.setState({
      gcsPath: path
    })

  }

  render() {
    return (
      <div className={styles.upload}>
        <h4>Upload audio file to GCS</h4>
        <div>
          <Dropzone 
            className={styles.dropzone}
            onDrop={this.onDrop.bind(this)}
          >
            <p>Dropping some file here, or click to select file to upload.</p>
          </Dropzone>
          <ul>
            {
              this.state.files.map(f => <li key={f.name}>{f.name} : {this.state.duration}s - {f.size} bytes</li>)
            }
          </ul>
          <Button onClick={this.onUpload.bind(this)} variant="outlined" color="primary">upload</Button>
        </div>
      </div>

    )
  }

}
Updata.css
:local .upload .dropzone {
  width: 300px;
  height: 200px;
  box-sizing: border-box;
  border-width: 2px;
  border-color: #666666;
  border-style: dashed;
  border-radius: 5px;
  vertical-align: top;
  margin-right: 2%;
}

こんな画面ができあがる

upload page

アップロードされたファイルは、firebaseコンソールのstorageタブから確認できる

firebase storage

参考

2022, Built with Gatsby. This site uses Google Analytics.