6

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?

1
  • 1
    I was having basically this exact same issue, in the middle of posting another question when I found this one, I got past it by uploading the base64 string as a dataType.TEXT. This does not feel like the right solution but for the small project I'm building not a big deal. Here is a repo with a small working example: postgres pic. master has datatype blob attempted, text-solution has the text solution. Commented Jul 27, 2018 at 22:23

1 Answer 1

2

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

}
Sign up to request clarification or add additional context in comments.

5 Comments

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
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
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')
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
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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.