简体   繁体   English

React Native 中的 Axios - 无法发布 Blob 或文件

[英]Axios in React Native - Cannot POST a Blob or File

I'm trying to post the raw data of a picture, using Axios, after taking it with react-native-image-picker .在使用react-native-image-picker picker 获取图片后,我正在尝试使用 Axios 发布图片的原始数据。

I successfully generated a blob using this piece of code:我使用这段代码成功生成了一个 blob:

const file = await fetch(response.uri);
const theBlob = await file.blob();

If I inspect the metadata of the blob it's all right: MIME type, size, and so on.如果我检查 blob 的元数据,那就没问题了:MIME 类型、大小等等。

However, when I try to POST it using Axios, using:但是,当我尝试使用 Axios 发布它时,使用:

axios({
    method: "POST",
    url: "https://my-api-endpoint-api.example.org",
    data: theBlob,
});

what I receive on the API side is this strange JSON payload:我在 API 端收到的是这个奇怪的 JSON 有效负载:

{"_data":{"lastModified":0,"name":"rn_image_picker_lib_temp_77cb727c-5056-4cb9-8de1-dc5e13c673ec.jpg","size":1635688,"offset":0,"type":"image/jpeg","blobId":"83367ee6-fa11-4ae1-a1df-bf1fdf1d1f57","__collector":{}}}

The same code is working fine on React, and I have the same behavior trying with a File object instead of a Blob one.相同的代码在 React 上运行良好,并且我尝试使用File对象而不是Blob对象时具有相同的行为。

I see in other answers that I could use something else than Axios, like RNFetchBlob.fetch() , but since I'm using shared functions between the React website and the React Native app, I'd really prefer an approach that allows me to use Axios and Blob s.我在其他答案中看到我可以使用 Axios 以外的其他东西,比如RNFetchBlob.fetch() ,但由于我在 React 网站和 React Native 应用程序之间使用共享函数,我真的更喜欢一种允许我的方法使用 Axios 和Blob s。

Is there some way to work around it?有没有办法解决它?

Updated answer更新的答案

As pointed out by @TJCrowder in the comments, there is a cleaner approach that works around the issue of the React Native host environment, without touching anything else on the code.正如@TJCrowder 在评论中指出的那样,有一种更简洁的方法可以解决 React Native 主机环境的问题,而无需触及代码上的任何其他内容。

It's enough to add this on the index.js file, before everything else:index.js文件中添加它就足够了,在其他所有内容之前:

Blob.prototype[Symbol.toStringTag] = 'Blob'
File.prototype[Symbol.toStringTag] = 'File'

I leave my original answer here under since it's a working alternative if one doesn't want to mess up with the prototype.我把我原来的答案留在下面,因为如果不想弄乱原型,这是一种可行的选择。

Original answer原始答案

The described behavior happens because the host environment of React Native does not handle the Blob type nicely: it will actually become just an object.发生上述行为是因为 React Native 的宿主环境不能很好地处理Blob类型:它实际上只是一个对象。

In fact, if you try to render toString.call(new Blob()) in a component, you'll see [object Blob] in a browser, but [object Object] in React Native.事实上,如果你尝试在组件中渲染toString.call(new Blob()) ,你会在浏览器中看到[object Blob] ,而在 React Native 中看到 [ [object Object]

The matter is that the default transformRequest implementation of Axios will use exactly this method ( toString.call ) to check if you're passing a Blob or some generic object to it.问题是 Axios 的默认transformRequest实现将完全使用此方法 ( toString.call ) 来检查您是否将Blob或一些通用对象传递给它。 If it sees you're passing a generic object, it applies a JSON.stringify to it, producing the strange JSON you're seeing POSTed.如果它看到您正在传递一个通用对象,它会对其应用JSON.stringify ,从而生成您看到的奇怪的 JSON POSTed。

This happens exactly here: https://github.com/axios/axios/blob/e9965bfafc82d8b42765705061b9ebe2d5532493/lib/defaults.js#L61这恰好发生在这里: https ://github.com/axios/axios/blob/e9965bfafc82d8b42765705061b9ebe2d5532493/lib/defaults.js#L61
Actually, what happens here is that utils.isBlob(data) at line 48 returns false, since it really just applies toString on the value and checks if it is [object Blob] , which as described above is not the case.实际上,这里发生的是第 48 行的utils.isBlob(data)返回 false,因为它实际上只是将 toString 应用于值并检查它是否是[object Blob] ,如上所述,情况并非如此。

The fastest workaround I see here, since you're sure you're passing a Blob , is just to override transformRequest with a function that just returns the data as it is, like this:我在这里看到的最快的解决方法,因为你确定你传递了一个Blob ,只是用一个只返回数据的函数覆盖transformRequest ,如下所示:

axios({
    method: "POST",
    url: "https://my-api-endpoint-api.example.org",
    data: theBlob,
    transformRequest: (d) => d,
});

This will just make the request work.这只会使请求工作。

You can send file into server using formData through axios API like below :您可以通过 axios API 使用formData将文件发送到服务器,如下所示:

const file = await fetch(response.uri);
const theBlob = await file.blob();
var formData = new FormData();
theBlob.lastModifiedDate = new Date();
theBlob.name = "file_name";
formData.append("file", theBlob);

axios.post('https://my-api-endpoint-api.example.org', formData, {
    headers: {
      'Content-Type': 'multipart/form-data'
    }
})

I actually had this problem recently (using Expo SDK 43 on iPhone).我最近实际上遇到了这个问题(在 iPhone 上使用 Expo SDK 43)。 I remember using axios over fetch because I had problems with uploading blobs with fetch in the past.我记得在 fetch 上使用 axios,因为过去我在使用 fetch 上传 blob 时遇到了问题。 But I tried it here and it just worked.但我在这里试过了,效果很好。

The context in this use case is downloading a giphy from url and then putting it on s3 with a signed request.这个用例中的上下文是从 url 下载一个 giphy,然后将它放在带有签名请求的 s3 上。 Worked on both web and phone.在网络和电话上都工作过。

const blob = await fetch(
          giphyUrl
).then((res) => res.blob());
fetch(s3SignedPutRequest, { method: "PUT", body: blob });

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM