Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

cannot upload pictures to postgres database

I am using React.js in the front-end and Express.js API in the backend to create a simple blog. I am adding a feature for the blog to have the ability to upload pictures and files. I keep getting this error.

{ error: invalid input syntax for type bytea
at Connection.parseE (C:\Users\Sophin\desktop\testingpostg\node_modules\pg\lib\connection.js:546:11)
at Connection.parseMessage (C:\Users\Sophin\desktop\testingpostg\node_modules\pg\lib\connection.js:371:19)
at Socket.<anonymous> (C:\Users\Sophin\desktop\testingpostg\node_modules\pg\lib\connection.js:114:22)
at emitOne (events.js:96:13)
at Socket.emit (events.js:188:7)
at readableAddChunk (_stream_readable.js:176:18)
at Socket.Readable.push (_stream_readable.js:134:10)
at TCP.onread (net.js:547:20)
name: 'error',
length: 93,
severity: 'ERROR',
code: '22P02',
detail: undefined,
hint: undefined,
position: '110',
internalPosition: undefined,
internalQuery: undefined,
where: undefined,
schema: undefined,
table: undefined,
column: undefined,
dataType: undefined,
constraint: undefined,
file: 'varlena.c',
line: '297',
routine: 'byteain' }
POST /api/learning 400 69.873 ms - 131




This is my form in React.js

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import LearningFirstComp from './components/LearningFirstComp';

class App extends Component {
 constructor(props) {
  super(props);
  this.state={
    inputTopValue:'',
    inputBottomValue:'',
    inputImageValue:{},
  }
  this.handleTopChange = this.handleTopChange.bind(this);
  this.handleBottomChange = this.handleBottomChange.bind(this);
  this.handleImageChange = this.handleImageChange.bind(this);
  this.handleSubmit = this.handleSubmit.bind(this);
}

handleTopChange(event) {
    this.setState({inputTopValue: event.target.value});
    console.log("changing")
}

handleBottomChange(event) {
    this.setState({inputBottomValue: event.target.value});
    console.log("descr")
}

handleImageChange(event) {
  this.setState({inputImageValue: event.target.value})
  console.log("imagechanged")
}

handleSubmit(event) {
    event.preventDefault();
    fetch('api/learning', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({
            topstuff: event.target.topstuff.value,
            bottomstuff: event.target.bottomstuff.value,
            pic1: event.target.myimage.value
        })
    })
}

render() {
 return (
  <div className="App">
    <form onSubmit={this.handleSubmit} encType="multipart/form-data">
      <input type="text" name="topstuff" placeholder="title" onChange={this.handleTopChange} value={this.state.inputTopValue} /> <br/>
      <input type="text" name="bottomstuff" placeholder="body" onChange={this.handleBottomChange} value={this.state.inputBottomValue} /><br/>
      <input type="file" name="myimage" onChange={this.handleImageChange} value={this.state.inputImageValue} /><br/>
      <input type="submit" value="Submit" />
    </form>
    <LearningFirstComp />

  </div>
  );
 }
}

export default App;

I tried using both "enctype" and "encType" in the form and both do not work.


The Schema for my postgresql database looks like this

    CREATE TABLE IF NOT EXISTS learningtable (
     id BIGSERIAL PRIMARY KEY,
     topstuff VARCHAR(255),
     bottomstuff VARCHAR(255),
     pic1 bytea
     );

I wonder if the problem is occurring because of the wrong datatype? I looked up and saw that to upload images below 1 MB we should usually use datatype "bytea" in postgresql.


This is my routes for the Express.js
    const express = require('express');
    const myrouter = express.Router();
    const controller = require('../controllers/learningController');
    const multer = require('multer');

    var storage = multer.diskStorage({
     destination: function (req, file, cb) {
       cb(null, 'uploads')
         }
       })

    var upload = multer({ storage: storage });

    myrouter.post('/', upload.single('myimage'), controller.create);

    module.exports = myrouter;

Here I am using multer to upload files and I think everything is in order here?


This is my Express.js controller

    const LearningObject = require('../models/learningmodel');
    const controller = {};

    controller.create = (req, res) => {
      LearningObject.create({
        topstuff: req.body.topstuff,
        bottomstuff: req.body.bottomstuff,
        pic1: req.body.pic1,
        })
          .then(jsonAfterAdding => {
            res.json({
             message: 'okay',
             jsonAfterAdding: jsonAfterAdding
              });
          }).catch(err => {
        console.log(err);
        res.status(400).json(err);
         });
       };

    module.exports = controller;

Finally, this is my Express.js Model

    const db = require('../db/config');
    const LearningObject = {};

    LearningObject.create = (randomparam) => {
      return db.one (
       `INSERT INTO learningtable
        (topstuff, bottomstuff, pic1)
          VALUES ($1, $2, $3) RETURNING *
        `,
      [randomparam.topstuff, randomparam.bottomstuff, randomparam.pic1]
      );
    };

  module.exports = LearningObject;

Any help would be greatly appreciated.

like image 425
craftdeer Avatar asked Feb 02 '26 08:02

craftdeer


1 Answers

First Problem (Client Side)

You are attempting to upload a file to the server with Javascript Fetch method while setting the request Content-Type to application/json. The request needs to be of type multipart/form-data.

Let's assign the html form an id for easier reference:

<form id="myform" ...

Now we need to grab the form element and assign it to a variable:

var form = document.getElementById("myForm");

To set the fetch xhr request to multipart/form-data Content-Type, we need to set the request body to a FormData object bound to the form element:

body: new FormData(form)

Second Problem (Server Side)

Accessing the uploaded image on req.body will not work. Multer's specialty is multipart/form-data requests and applies the parsed request body a little differently then the body-parser middleware.

We can access the uploaded image data on the request file object. We will grab the image binary data through the buffer property.

pic1: req.file.buffer,

Third Problem (React DOM)

Unlike the browser DOM, React DOM requires special handling. Since we can't reference the form tag directly, we need to store form input values in the state on change.

Storing the file input value is not enough in the case of a file. We need to also store the files object as well. And since we only expect one file reference, we will just grab the first object in the array.

this.setState({inputImageValue: event.target.value, 
                inputImageFile: event.target.files[0]})

Now in the handleSubmit event function we build our FormData object and append the image (append order is not important):

var formData = new FormData();
formData.append('myimage', this.state.inputImageFile);

Then we append the rest of our input fields:

formData.append('...', this.state...);

And finally we assign the formData to the body of our request:

body: formData
like image 136
Scriptonomy Avatar answered Feb 03 '26 22:02

Scriptonomy