How to render blob image in vue.js stored in database

11,286

First, be sure you're getting a valid base64 string: https://codebeautify.org/base64-to-image-converter

Then try defining a getter to the Item model

const Item = sequelize.define('item', {
    ...
    image: {
        type: Sequelize.BLOB('long'),
        allowNull: true,
        get () { // define a getter
            const data = this.getDataValue('image')
            return data ? data.toString('base64') : ''
        },
        set(val) {
          this.setDataValue('image', val);
        }
    },
    ...
}

Computed property

imgURL () {
    return this.item.image
        ? 'data:image/png;charset=utf-8;base64,' + this.item.image 
        : '' // some default image

}
Share:
11,286
paceaux
Author by

paceaux

I'm a Principal Solutions Consultant for EXLRT. I build websites for companies on the Fortune 500 and 100 list. For fun, I study foreign languages. My degrees are in French and Spanish, and I'm currently learning Hebrew, Arabic, and Portuguese. But I've dabbled with Mandarin Chinese, Russian, and Arabic in the past. I'd like to work in and around translation or natural language processing.I built my own part-of-speech tagger for fun.

Updated on June 23, 2022

Comments

  • paceaux
    paceaux almost 2 years

    I am using Vue.js in the front-end. I have Node.js, Express, PostgreSQL (with Sequelize ) on the backend.

    I am storing an item in the database that includes a thumbnail image.

    Database Model

    const Item = sequelize.define('item', {
        id: {
            type: Sequelize.INTEGER,
            primaryKey: true,
            autoIncrement: true
        },
        name: {
            type: Sequelize.TEXT,
            allowNull: false,
        },
    
        image: {
            type: Sequelize.BLOB('long'),
            allowNull: true,
        },
    

    Database-wise, the image is being stored as a Blob and I think this is fine (and yes, I am aware that it's not best-practice to put images in a database).

    I observe in the browser that the object that I am accessing in my Vue template with this.item.image is an Object of the type Buffer.

    Console shows this is an Object with type Buffer

    Adding to Database

    I add the item to the database in the browser with this in my vue template:

         <label for="image" class="itemCreate__field itemCreate__field--image">
           <span class="itemCreate__fieldLabel">Image</span>
           <input id="file"  type="file" accept="image/*" @change="onFileChange"/>
            <img v-if="itemPreviewImage" :src="itemPreviewImage" />
         </label>
    

    And that HTML relies on these methods:

         onFileChange(evt) {
            const files = evt.target.files || evt.dataTransfer.files;
            if (!files.length) return;
            this.createImage(files[0]);
    
        },
        createImage(file) {
            const image = new Image();
            const reader = new FileReader();
    
            reader.onload = evt => {
                this.itemPreviewImage = evt.target.result;
                this.item.image = evt.target.result;
            }
    
            reader.readAsDataURL(file);
        },
    

    I have this in the vue template that renders the image:

            <div v-if="item.image">
                <img :src="imgUrl" alt="Picture of item"/>
            </div>
    

    Rendering from Database

    I have tried the following approaches, which do not work:

    createObjectUrl borrowed from here:

        imgUrl(){
          const objUrl = window.URL.createObjectURL(new Blob(this.item.image.data));
    
          return objUrl;
        }
    

    Creating a base64 string borrowed from here:

        imgUrl(){
            const intArray = new Uint8Array(this.item.image.data);
            const reducedArray = intArray.reduce((data, byte) => data + String.fromCharCode(byte), '');
    
            const base64String = `data:image/png;base64, ${btoa(reducedArray)}`;
    
    
            return base64String;
        }
    

    Creating a new Uint8Array and then getting an objectUrl (borrowed here):

    imgUrl(){
      const arrayBuffer = new Uint8Array(this.item.image);
      const blob  = new Blob([arrayBuffer], {type: "image/png"});
    
      return window.URL.createObjectURL(blob);
    
    }
    

    In all cases (including some attempts with FileReader), I get broken images. I don't get errors in the console, though.

    I think the issue is that I am not submitting correct data to the database.

    I am sending an Ajax request that has the File attached as a property, and I should probably convert it to ¿something else?

  • paceaux
    paceaux about 6 years
    I tried that exact code and ended up with <img src="data:image/png;charset=utf-8;base64[object Object]" alt="Picture of item"> I think I need to somehow stringify that object in my computed prop
  • paceaux
    paceaux about 6 years
    Upon Closer review, it looks like the base64 apparently isn't valid. So that means there's a problem with how I'm submitting the image to the database
  • Frondor
    Frondor about 6 years
    You should be passing a Buffer to item.image before saving it. Add the code to the question so we can check if that's ok. Also, try setting long length to Sequelize.BLOB('long')
  • paceaux
    paceaux about 6 years
    I've updated the question to include how I'm adding the file. I also modified my model. How can I be sure that I'm passing a buffer? in my createImage method in my "add" template, it doesn't work even if I try this.item.template = file ... This is my ignorance as I'm not sure I know when a file becomes a buffer and vice versa
  • ContextCue
    ContextCue almost 6 years
    As a side note I tried these things: passed ArrayBuffer, converted with FileReader / passed base64 string (w and wo substr) / passed just the file from new FileReader() / passed the file from fs-web fileRead... a few of these options could be decoded with atob but the string was not valid after decoding.