【ReactNative開発】AndroidでFetch APIを使った画像アップロードできない(Network Request Failed / Bad Format)【超絶苦戦】

こんにちは。今週は、画像アップロードと戦い続けて終わった一週間でした・・

結局正しい解決方法は分かっていないのですが、いくつかの問題に遭遇したので、苦闘の記録を残しておこうと思います。

前提

  • React Native
  • Expo
  • expo-image-picker(画像の選択)
  • Androidエミュレータでテスト

問題1:Network Request Failedエラー

Expo-image-pickerで選択した画像をfetch apiを使ってクラウド上のストレージにアップロードしようとしたところ、Network Request Failedエラーが発生。

    const formData = new FormData();
    const dataBlob = await fetch(params.uri).then((r)=>r.blob());
    formData.append('data', dataBlob);

    const url = 'xxx'
    await fetch (url,{
        method: 'POST',
        headers:{
                      ・・・・
        },
        body:formData
    })
Network request failed

Fetch APIのドキュメントをみると、blob (file.input[0])も送れるって書いてるんだけどなぁ・・

https://developer.mozilla.org/ja/docs/Web/API/Fetch_API/Using_Fetch

原因と解決方法

調べてみると、Androidエミュレータでこのエラーが出るという議論が結構あった。iOSでテストしてないけど、そのあたりからするに、Androidエミュレータに固有の問題なのかもしれない。

以下の議論を元に、blob自体ではなくimageのURIを送る方法でもいけるらしいことがわかったので、この実装に変更して、Network Request Failedエラーは解消することができた。

https://stackoverflow.com/questions/42521679/how-can-i-upload-a-photo-with-expo

が、すぐに次の問題にぶち当たる。

問題2:アップロードされた画像のフォーマットが正しくない

問題1の解決方法の通り、結局FormDataに以下の形式で画像情報をセットしてアップロードした。

    formData.append('image',{
        uri:imageUri,
        name:"a.jpg", 
        type:dataType
    } as any);

サーバーサイドはこんな感じ。

<Node.js>


        // Parse Blob Data
        context.log("# step1 : require parse-multipart")
        var multipart = require("parse-multipart");

        context.log("# step2 : buffer")
        var bodyBuffer = Buffer.from(req.body);
        //var bodyBuffer = req.body
        context.log(bodyBuffer)
        context.log("# step3 : boundary")
        var boundary = multipart.getBoundary(req.headers['content-type']);
        context.log(boundary)
        context.log("# step4 : parse")
        var parts = multipart.Parse(bodyBuffer, boundary);
        context.log(parts)

        その後Parseしたデータをストレージに保存・・・

が、ストレージに保存された画像をダウンロードしてきて開こうとすると、”ファイルが壊れているか、”プレビュー”が認識しないフォーマットを使用している可能性があります”とでた。

どこで何がどうなったんだ・・・

原因と解決方法

まず、サーバーサイドでストレージに保存する直前の画像データを表示させてみた。

・・・ん?!data部の16進数表記のバイナリデータがff d8から始まっていない・・!?

こちらの記事が参考になりましたが、JPEGデータの場合、FF D8から始まる決まりがあります。

https://qiita.com/kazuaki0213/items/d3e71fe203b4f1d19abc

が、私のプログラム上は、FF D8の前に、0d 0aがくっついているではありませんか・・・

0d 0aは、改行コード (CR + LF)を示すコードです。

どうやらここに意図せぬ改行コードが入ってしまっていることで画像が正しいフォーマットと見做されていないのでは、という仮説に辿り着きました。

では、次はこの改行コードがどこで埋め込まれているのか?という疑問に当たります。

試しにクライアントアプリ側で、image-pickerで取得した画像の16進数バイナリ情報を出力してみました。

あれ・・・ここでは改行コードは入っていない・・。

ということは、画像データをformdataにセットしてから、サーバサイドでparseするまでのどこかで改行コードが混入しているっぽい・・・。

いろいろとデバッグを頑張りましたが、結果として、混入している箇所は特定に至らず・・

ドンピシャなIssueではないですが、Expo-image-pickerのIssueでAndroidの場合だけ改行コードが入る、といった報告もあったことから、iPhoneでは正常に動くか試してみた。

https://github.com/expo/expo/issues/1522

画像は違いますが、(Androidの場合画像を変えても同じだったのでそこは問題ではないと判断)iPhoneの場合は改行コードが混入していない・・・!

うーん、結局原因がよく分からないですが、Android固有で発生する問題の模様・・・

今回は、iOSでアプリを動かすことが優先だったので、一旦コードの変更は行わず、開発を継続することに。

根本的な原因については、Stack Overflowで聞いてみているので、今後何かアップデートがあれば良いな・・・

https://stackoverflow.com/questions/69379631/how-to-upload-image-from-react-native-expo-app-to-azure-blob-storage

この問題に1週間以上費やしてしまったよ・・・辛い・・

同じ問題に対処されている方のヒントになれば幸いです。もし、この記事が少しでもお役に立ちましたら、下のいいねボタンをポチっていただけると励みになります・・!

この記事を気に入っていただけたらシェアをお願いします!

コメントを残す

メールアドレスが公開されることはありません。

ABOUT US

Yuu113
初めまして。Yuu113と申します。 兵庫県出身、東京でシステムエンジニアをしております。現在は主にデータ分析、機械学習を活用してビジネスモデリングに取り組んでいます。日々学んだことや経験したことを整理していきたいと思い、ブログを始めました。旅行、カメラ、IT技術、江戸文化が大好きですので、これらについても記事にしていきたいと思っています。