ikreb
ikreb

Reputation: 2785

How to show a with axios loaded image in vue?

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>

Upvotes: 1

Views: 2941

Answers (1)

Daniel
Daniel

Reputation: 35724

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/[email protected]/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/[email protected]/dist/axios.min.js"></script>
<app>
 <img :src="imageSrc" v-if="imageSrc"/>
</app>

Upvotes: 2

Related Questions