简体   繁体   中英

How to GET binary image correctly using redux-observable?

I am trying to GET image using redux-observable.

Based on Microsoft Graph API , it returns binary data of the requested photo.

I succeed getting the image using Postman (it shows the image in the result since it is binary).

However, when I try to use redux-observable , response I got is always null , and responseType is always json no matter what I give for Content-Type like image/jpeg , text/plain , application/json .

export const getAvatarEpic = (action$, store) =>
  action$
    .ofType(GET_AVATAR)
    .mergeMap(action =>
      ajax
        .get(
          'https://graph.microsoft.com/v1.0/me/photo/$value',
          {
            // 'Content-Type': 'image/jpeg',
            'Authorization': 'Bearer ' + myToken
          }
        )
        .do(res => console.log(res))  // <- the result
        .map(getAvatarSucceed)
        .catch(getAvatarFailed)
    );

Here is what I got. Maybe I should use something else instead of ajax.get ?

{
  "originalEvent": {
    "isTrusted": true
  },
  "xhr": {},
  "request": {
    "async": true,
    "crossDomain": false,
    "withCredentials": false,
    "headers": {
      "Authorization": "Bearer hereIsMyToken",
      "X-Requested-With": "XMLHttpRequest"
    },
    "method": "GET",
    "responseType": "json",
    "timeout": 0,
    "url": "https://graph.microsoft.com/v1.0/me/photo/$value"
  },
  "status": 200,
  "responseType": "json",
  "response": null
}

I want to first make sure it becomes clear that redux-observable and RxJS are separate things. This question is actually an RxJS question, as is nearly all questions you'll come across (even when using redux-observable) since redux-observable is small and really defers almost everything to idiomatic Rx. This is important because when asking questions if you present and phrase them as just Rx questions you'll find much more help and resources since it's a larger community overall. Hope this helps!


If you're using the built-in ajax utilities for RxJS v5, you'll need to use the regular ajax() helper, not the shorthand ajax.get() one.

Then you can provide responseType: 'arraybuffer' to get the image as a binary data:

export const getAvatarEpic = (action$, store) =>
  action$
    .ofType(GET_AVATAR)
    .mergeMap(action =>
      ajax({
        url: 'https://graph.microsoft.com/v1.0/me/photo/$value',
        headers: {
          'Authorization': 'Bearer ' + myToken
        },
        responseType: 'arraybuffer'
      })
      .do(res => console.log(res))  // <- the result
      .map(getAvatarSucceed)
      .catch(getAvatarFailed)
    );

Since this question actually is unrelated to redux-observable, here's a working example demonstrating getting the binary data and then creating an <img> with it:

https://jsbin.com/mimijot/edit?js,output

import { ajax } from 'rxjs/observable/dom/ajax';

ajax({
  url: 'https://proxy.apisandbox.msdn.microsoft.com/svc?url=https%3A%2F%2Fgraph.microsoft.com%2Fv1.0%2Fme%2Fphoto%2F%24value',
  headers: { 'Authorization': 'Bearer {token:https://graph.microsoft.com/}' },
  responseType: 'arraybuffer'
})
  .subscribe(res => {
    console.log(res);
    const buffer = res.response;
    const blob = new Blob([buffer], { type: 'image/jpeg' });
    const url = URL.createObjectURL(blob);
    const img = document.createElement('img');

    img.src = url;
    document.body.appendChild(img);
  });

When building the Graph explorer with Angular 4, I encountered the same issue. In our case, when requesting images we have to set the responseType parameter to ArrayBuffer instead of the default value.

https://github.com/microsoftgraph/microsoft-graph-explorer/blob/master/src/app/graph-service.ts#L54

case "GET": // for all JSON requests to Graph
        return this.http.get(query, {headers: requestHeaders}).toPromise();
case "GET_BINARY": // when fetching images
        return this.http.get(query, {responseType: ResponseContentType.ArrayBuffer, headers : requestHeaders}).toPromise();

And then when processing the response, we get the blob URL and set the image element's src:

 let blob = new Blob( [ result.arrayBuffer() ], { type: "image/jpeg" } );
 let imageUrl = window.URL.createObjectURL( blob );

 const imageResultViewer = <HTMLImageElement>document.getElementById("responseImg");
 imageResultViewer.src = imageUrl;

It looks like Rx.DOM.ajax has the responseType property, so I recommend changing that but I'm not an RxJS expert! More info about XMLHttpRequest.responseType can be found at https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType

Final code:

export const getAvatarEpic = (action$, store) =>
action$
.ofType(GET_AVATAR)
.mergeMap(action =>
  aja:@{
    url: 'https://graph.microsoft.com/v1.0/me/photo/$value',
    responseType: 'arraybuffer',
    headers: {
      // 'Content-Type': 'image/jpeg',
      'Authorization': 'Bearer ' + store.getState().auth.token
    }
  })
    .map(getAvatarSucceed)
    .catch(getAvatarFailed)
); 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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