1

I have a GET request with axios and get a .png file back and want to show this inside my template. I can't use a path url, because the image is each time differently.

This is my fastapi route.

from io import BytesIO
from fastapi.responses import Response


@app.get("/image", response_class=Response)
def load_image():
    ...
    buffer = BytesIO()
    img.save(buffer, format="PNG")

    return Response(content=buffer.getvalue(), media_type="image/png")

This is the vue component:

<script>
export default {
  name: "Example",
  data() {
    return {
      image: null;
    };
  },
  methods: {
    async loadImage() {
      const url = "/image";
      const response = await $axios.get(url, { responseType: "arraybuffer" });
      if (response.status == 200) {
        const base64string = btoa(String.fromCharCode(...new Uint8Array(response.data)));
        console.log(base64string); // -> this is a empty string
        this.image = 'data:image/png;base64,' + base64string;
      }
  },
  mounted() {
    this.loadImage();
  },
};
</script>

<template>
  <div>
    <img :src="image" title="Image" />
  </div>
</template>
ikreb
  • 2,133
  • 1
  • 16
  • 35
  • 1
    Can you post what `this.image` actually contains? Either from the response or `console.log` might be helpful. As for `` tag, you need a string for `src` attribute. – Zarko Sep 28 '22 at 22:37
  • ``this.image``= ``"�PNG \n\n IHDR���G!�IDATx��An�HE?G�,}�E�A��H}�(9@i@gQ�"ewϠ���........"`` – ikreb Sep 28 '22 at 22:41
  • 1
    That is what I expected. So you are right. The response is the problem. I guess if you access the URL directly in the browser you are getting the image as it should be. IMO, use base64 encoding for the images if possible ( commonly used with dynamic images). You will get a base64 URL which you can directly use in `src` – Zarko Sep 28 '22 at 22:47
  • 1
    Related answers can also be found [here](https://stackoverflow.com/a/73726553/17865804), as well as [here](https://stackoverflow.com/a/73264904/17865804) and [here](https://stackoverflow.com/a/73240097/17865804). – Chris Sep 29 '22 at 04:20
  • @Chris: Thanks for your reply. I tried the answer, but doesn't work. I updated my question. – ikreb Sep 29 '22 at 08:32
  • 1
    Please have a look at [this answer](https://stackoverflow.com/a/71639658/17865804) and [this answer](https://stackoverflow.com/a/71643439/17865804) on how to return a `Response` with image bytes held in an in-memory buffer from FastAPI backend. – Chris Sep 29 '22 at 10:52

1 Answers1

1

You can...

  • get the data as a blob by passing { responseType: "blob" } to axios
  • convert the blob to base64 with FileReader (used blobToData function from https://stackoverflow.com/a/63372663/197546)
  • use the base64 data as the image src

example:

const app = Vue.createApp({
  data() {
    return {
      imageSrc: null,
    };
  },
  methods: {
    async loadImage() {
      const url =
        "https://images.unsplash.com/photo-1664300067908-84e8beb52a8f?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwyNXx8fGVufDB8fHx8&auto=format&fit=crop&w=500&q=60";
      const response = await axios.get(url, { responseType: "blob" });
      if (response.status == 200) {
        const base64data = await blobToData(response.data);
        this.imageSrc = base64data;
      }
    },
  },
  mounted() {
    this.loadImage();
  },
});

app.mount("app");

function blobToData(blob) {
  return new Promise((resolve) => {
    const reader = new FileReader()
    reader.onloadend = () => resolve(reader.result)
    reader.readAsDataURL(blob)
  })
}
<script src="https://unpkg.com/vue@next/dist/vue.global.prod.js"></script>
<script src="https://unpkg.com/axios@0.27.2/dist/axios.min.js"></script>
<app>
 <img :src="imageSrc" v-if="imageSrc"/>
</app>

As Chris pointed out, you can also...

  • get the data as an array buffer by passing { responseType: "arraybuffer" } to axios
  • convert array to base64 data using btoa(String.fromCharCode(...new Uint8Array(response.data)))
  • build the src data by adding prepending the content type to the base64 data

example:

const app = Vue.createApp({
  data() {
    return {
      imageSrc: null,
    };
  },
  methods: {
    async loadImage() {
      const url =
        "https://images.unsplash.com/photo-1664300067908-84e8beb52a8f?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwyNXx8fGVufDB8fHx8&auto=format&fit=crop&w=500&q=60";
      const response = await axios.get(url, { responseType: "arraybuffer" });
      if (response.status == 200) {
        const b64 = btoa(String.fromCharCode(...new Uint8Array(response.data)));
        const imgData = "data:" + response.headers['content-type'] + ";base64," + b64;
        this.imageSrc = imgData;
      }
    },
  },
  mounted() {
    this.loadImage();
  },
});

app.mount("app");
<script src="https://unpkg.com/vue@next/dist/vue.global.prod.js"></script>
<script src="https://unpkg.com/axios@0.27.2/dist/axios.min.js"></script>
<app>
 <img :src="imageSrc" v-if="imageSrc"/>
</app>
Daniel
  • 34,125
  • 17
  • 102
  • 150
  • Getting a `blob` object and then converting it into base64 format makes little sense. You can simply create a URL out of the `blob` object and use that as the image `src`; otherwise, if you would like to get the image in base64 format in the first place, use `responseType: "arraybuffer"` and [`btoa()`](https://developer.mozilla.org/en-US/docs/Web/API/btoa) method to create a base64-encoded string. Please have a look at [this answer](https://stackoverflow.com/a/71324775/17865804) for more details and examples. – Chris Sep 29 '22 at 07:09