Use react to store binary data to SQL Server - node.js

I'm working on a feature that allows the user to upload a pdf to a SQL Server database. The user clicks a button to open a file dialog and selects the pdf they want to upload. The SQL Server database takes a varbinary(max).
I'm using base64 to encode the file, however I am getting an error saying
RequestError: Implicit conversion from data type varchar to varbinary(max) is not allowed. Use the CONVERT function to run this query
Looks like I'm sending a string, not binary.
Here is the code I'm using:
React.js
import React, { Component } from 'react';
import './styles.css';
import base64 from 'base-64';
class SaveSDS extends Component {
constructor() {
super();
this.state = { user: {} };
this.chemNameField = React.createRef(); // create ref for first name field
}
state = {
file: null
}
handleFile(e) {
e.preventDefault();
let file = e.target.files[0];
this.setState({file: file});
}
handleUpload(e){
e.preventDefault();
const data = new FormData();
let file = this.state.file;
data.append('file', file)
var r = new FileReader();
fetch('http://localhost:5000/db', {
method: 'POST',
headers: { "Content-Type": "application/json" },
mode: 'no-cors',
body: JSON.stringify({
chemName: this.chemNameField.current.value,
chemPDF: base64.encode(data)
})
})
.then((res) =>{
console.log(res)
});
}
render() {
return (
<div className="submitForm">
<div style={{padding: "10px"}}>
<input className="fileButton" type="file" accept=".pdf" name="file" onChange={(e) => this.handleFile(e)} />
</div>
<input type="text" style={{width: "50%"}} placeholder="Enter chemical name" ref={this.chemNameField} />
<br />
<button type="button" onClick={(e) => this.handleUpload(e)}>Upload</button>
</div>
);
}
}
export default SaveSDS;
And here is what I'm using on the express side:
Server.js
const express = require('express');
const bodyParser = require('body-parser');
var sql = require("mssql");
const app = express();
app.use(bodyParser.json());
app.use(express.json({
type: ['application/json', 'text/plain']
}));
var config = {
user: 'user',
password: 'pass',
server: 'localhost',
database: 'Master'
};
app.post('/db', function(req, res) {
console.log(req.body)
res.set('Access-Control-Allow-Origin', '*');
let connection = new sql.ConnectionPool(config, function(err) {
let request = new sql.Request(connection);
request.query("insert into chemicals (chemName, chemPDF) values ('" + req.body.chemName + "', '" + req.body.chemPDF + "')");
});
});
const port = process.env.PORT || 5000; //get the process port
app.listen(port, () => console.log(`Server running on port ${port}`));
Is it even possible to send binary to the backend from react, or would it be easier to convert to binary on the backend? Maybe my approach is totally wrong.
Basically I want the user to be able to upload a file, then allow other users to open and view that file. I've done this in C# by uploading binary to SQL Server, then decoding it so people can view it. Not sure if that translates to node/react.
Thanks!

Related

React + Nodejs/Express App to display products

I've got a simple React App with Nodejs/Express server.
Right now I'm trying to obtain an ID value from Serialport and compare that ID with the ID from an external webservice and retrieve the data I get from the webservice and display it on the frontend.
I've got the first part working and right now I can compare both ID's and fetch the data but can't figure out how to display it in the React frontend.
This is my server index.js:
const express = require('express');
const path = require('path');
const app = express();
app.use(express.static(path.join(__dirname + "/public")));
const PORT = process.env.PORT || 5000;
const scanProducts = () => {
const scan = require('./actions/scanProduct');
scan.scanner();
//can't reach data here
}
app.listen(PORT);
scanProducts();
This is my scanProduct.js
const { SerialPort } = require('serialport');
module.exports = {
scanner: function(){
const port = new SerialPort({ path: 'COM6', baudRate: 9600, autoOpen: false });
port.open(function (err) {
if (err) {
return console.log('Error opening port: ', err.message)
}
port.write('main screen turn on')
})
port.on('open', function() {
setInterval(function(){
const portReader = port.read();
if(portReader != null){
const sensorVal = Buffer.from(portReader).toString();
const soap = require('soap');
const url = 'http://example.com?wsdl';
soap.createClient(url, function(err, client) {
client.GetProductById({ UserId: "1", ProductId: sensorVal }, function(err, result) {
if(err) return console.log(err);
console.log(result.GetProductByIdResult);
return result.GetProductByIdResult; //returns nothing into index.js
});
});
}
}, 500);
})
}
}
Then I would like to add each product from the server into this custom component, right now it displays static data fetched from a basic json file.
This my component ProductsList.js:
import ProductRow from "./ProductRow";
import './ProductsList.css';
const ProductsList = props => {
return(
<>
<div id="products-wrapper" className="w-9/12 h-full px-20 py-20 flex flex-col overflow-x-hidden overflow-y-scroll ">
<div className="w-full h-auto my-2 px-3 py-3 font-semibold grid grid-cols-3 bg-blue-600 rounded-xl">
<div className="w-[60%] text-left">Produto</div>
<div className="w-[20%] text-left">Quant.</div>
<div className="w-[20%] text-left">Preço</div>
</div>
{props.items.map(product => <ProductRow name={product.name} quantity={product.quantity} price={product.price}/>)}
//here is where I need to add each product from the server
</div>
</>
);
}
export default ProductsList;
Any idea what I might be missing?
Any help would be greatly appreciated.
A sample code for your project
I put some comments, I hope it will be easy to follow
I wrote some remarks at the end of the code too
Backend
For the index.js (main) file of your backend
const EventEmitter = require('events');
var cors = require('cors')
// Init express app
const app = require('express')();
// Import your scanner utility
const scan=require('./scanProduct')
/*Enabling CORS for any origin (host and port) to test the service from a another origin (react client) .
It depends how you deploy your services */
app.use(cors({
origin:'*'
}))
// Init the http server
const server = require('http').createServer(app);
// Init the websocket library
const io = require('socket.io')(server);
// Define some plain emitter
const emitter=new EventEmitter()
// Pass the event emitter in it
scan.scanner(emitter)
// Event fired when the websocket has been established
io.on('connection', socket => {
// When the scanProduct fire an event on scanner channel
emitter.on("scanner",function(data){
// Emit the data on the websocket for the react client
// You can define whatever name you prefer, here also scanner
socket.emit('scanner',data)
})
});
//Listening on port 8080
server.listen(8080);
And for the scanProduct.js file i just "mocked" your implementation with setInterval every 2 secs in my case
module.exports = {
scanner: function(eventEmitter){
// Read serial port every 2000ms
setInterval(()=>{
/*
- Read on serial port
- fetch the data from the soap web service
*/
// just some data to send but should be from your workflow
const productResult=new Date().toISOString()
// you emit on the channel you defined in the index.js file
eventEmitter.emit("scanner",productResult)
},2000)
}
}
The depedencies I am using on the backend
"dependencies": {
"cors": "^2.8.5",
"express": "^4.18.2",
"socket.io": "^4.6.0"
}
Frontend
For the front-end, I used npx create-react-app to init the project,
And I edited only one file, the App.js file
import React, { useState, useEffect } from 'react';
import io from 'socket.io-client';
// Websocket initialization, use your config or .env file to set up the url
const websocket = io(
"http://localhost:8080",
{ transports: ['websocket'] }
)
function App() {
//State keeping all the products we are displaying
const [products, setProducts] = useState([])
//When App component is mounted
useEffect(() => {
// Just to log the connection
websocket.on('connection', () => {
console.log("Websocket connected")
})
// When websocket receive data on the scanner channel, emitted from the backend
websocket.on("scanner", (product) => {
// Update the products with new ones
setProducts(prevProducts => [...prevProducts, product])
})
}, [])
return (
<div>
<div>
Number of products : {products.length}
</div>
{
products.map((data) => {
return (
<div>
{data}
</div>
)
})
}
</div>
);
}
export default App;
My sample code as it is will send data every 2000ms and we will have en enormous list in no time ^^'.
For your use case, I do not know if it is relevant to send data every 500ms to the client.
Do you have continuous flow of valuable/usefull data on the serial port ? If you really have valuable data every 500ms, another approach would be more appropriate, like batching. The idea will be to save somewhere the products you collected every 500ms, and from titme to time, every 10 secondes for example, you send this list of new products to the client.
If the payload on the serial port arrives randomly, you need to build some logic into your setInterval function, and only trigger an event when necessary.
The sample of course lacks some features. You only have new products coming from the websocket, but no history. If you refresh your browser page or if the socket is closed for whatever reason, you are going to miss some products. If history is important, you need some persistence layer.

Uploading files from React to Node - cannot access File object

I'm struggling to access the FileList from an input type="file" submitted from a React frontend to a Node backend. From my browser console I can see that the desired list of files is being submitted as the form data.
Eventually worked out that the FileList looks like an array but isn't! So got the length of it using Object.keys (interestingly other answers recommended here didn't work for me).
But no matter what I try I can't access the individual File objects... I can now cycle through the items in the FileList but I need to be able to get the filename to upload/move it to a new location on my server. It looks like they're empty objects in node but the browser is showing that it's sending the information I need.
Why are the objects I am iterating through empty?
I'm stumped.
Thanks for your help!
Browser Console Output
Node.js
router.post("", (req, res) => {
var tempFiles = req.body.files;
var fileCount = Object.keys(tempFiles).length;
console.log("FileList Keys: " + Object.keys(tempFiles));
console.log("Length: " + Object.keys(tempFiles).length);
console.log("Object Length: " + Object.keys(tempFiles['0']).length);
// Loop through files
for (let i = 0; i < fileCount; i++) {
let file = tempFiles[i];
console.log(tempFiles[i]);
}
}
Node console output:
FileList Keys: 0,1
Length: 2
Object Length: 0
{}
{}
React Form
import React from 'react';
import axios from 'axios';
import {useState} from 'react';
import { useForm } from "react-hook-form";
import { Stack, Input ,InputRightElement, InputGroup, Button, FormControl, FormLabel, FormHelperText, Checkbox, Radio, RadioGroup } from '#chakra-ui/react';
import BasicBanner from '../../core/components/banners/BasicBanner';
export default function UploadEvidence(props) {
const [data, setData] = useState("");
const [formMessage, setFormMessage] = useState("");
const { register, formState: { errors }, handleSubmit } = useForm();
const onError = (errors, e) => console.log(errors, e);
const onSubmit = (data, e) => {
console.log(data);
axios.post('http://localhost:5000/api/uploads', data)
.then(function (response) {
console.log(response);
setFormMessage("Upload successful");
})
.catch(function (error) {
console.log(error);
setFormMessage("Error uploading");
});
}
return (
<form onSubmit={handleSubmit(onSubmit, onError)} enctype="multipart/form-data">
<Stack spacing="10">
{formMessage &&
<BasicBanner message={formMessage} />
}
<Input
type="file"
accept="pdf/*"
multiple
{...register("files", {required: true })}
aria-invalid={errors.active ? "true" : "false"}
/>
</Stack>
<FormControl mt={4}>
<Button type="submit" colorScheme='blue'>Save</Button>
</FormControl>
</form>
)
}
Node server.js
const express = require('express')
const PORT = process.env.PORT || 5000
const app = express()
const bodyParser = require("body-parser");
const fileupload = require("express-fileupload");
var cors = require('cors');
app.use(cors());
app.use(fileupload());
app.use(express.static("files"));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
... Routes ...

How to show data in frontend after you send data in express js

In my backend I want to send the data in this code express.js
const Joi = require('joi')
const people = [
{id:1,username:"Youw"},
{id:2,username:"thuneer"}
]
app.get('/send',(req,res) => {
res.json({data:people})
})
app.post('/send',(req,res) => {
const {error} = validateCourse(req.body)
if (error) {
return res.status(400).json({data:"Username length should be atleast 5"})
}
res.status(200).json({data:people})
})
function validateCourse(params) {
const schema = Joi.object({
username:Joi.string().min(5).required(),
})
return schema.validate(params)
}
So here I know you guys understand it since this is the basic. The username supposed to be atleast 5 and then if not then error will be the data..but How I will show this in frontend? My friend suggested this code to me but I don't know how I will implemented or arrange this
const temp = await fetch("http://localhost:3939/send", { method: "POST", body: JSON.stringify(data) })
const res = await temp.json();
const data = JSON.parse(data);
if (res.errors) {
// handle errors here
if (res.errors.username) {
const usernameError = document.getElementById('username_error');
usernameError.innerText = res.errors.username;
}
}
<form action="/send" method="POST">
<input type="text" name="username" id="user" class="user-text">
<button type="submit">submit</button>
</form>
<div id="username_error"></div>
I don't wanna write this in ajax cause I will write sooner in my reactjs so I want the code to be more general so that I can write it in ReactJS.

Implementing a collaborative text editor using nodejs/react/socket but encountering problems because of slow heroku servers

I've tried making a collaborative editor using socket.io with reactjs frontend and node backend. Here's the piece of logic which I think is causing problems....
When a client starts typing on the editor, I've used onInput event to emit a socket response say "typing" which carries the complete text on the editor inside data object at the moment client presses a key. Now server catches this typing event and in response to that, emits another socket response called "typed" which contains the same data but the server sends this response to all the clients connected to the server.... Now all clients receive this event inside componentDidMount and then update the state variable "codeValue" which updates the editor content for all the clients.
There are two problems, first one that on one single typing event, server is emitting numerous typed events ( it happens only in heroku server and not on local host ) and the other problem is that heroku servers are slow and before the server sends response to update the state of clients, clients had already entered more text on the editor which simply vanishes when the state is updated.....
FRONTEND CODE:
import React from "react";
import { Dropdown } from "semantic-ui-react";
import languages from "../utils/languages";
//Styles
import "../styles/app.css";
//Editor
import * as ace from "ace-builds";
// import SocketIOClient from "socket.io-client";
import "ace-builds/src-noconflict/mode-c_cpp";
import "ace-builds/src-noconflict/theme-github";
import "ace-builds/src-noconflict/ext-language_tools";
import AceEditor from "react-ace";
let check = true;
let ld;
// const endpoint = "http://localhost:4676";
// const socket = SocketIOClient(endpoint, { transports: ["websocket"] });
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
codeValue: languages[0].template,
currentLang: languages[0].key,
};
this.codeEditor = React.createRef();
this.fireTyping = this.fireTyping.bind(this);
this.onDDChange = this.onDDChange.bind(this);
this.runCode = this.runCode.bind(this);
this.handleOutput = this.handleOutput.bind(this);
}
componentDidMount() {
this.props.socket.on("typed", (data) => {
console.log(35, data.text)
this.setState({
codeValue: data.text,
});
check = true;
console.log(check)
});
this.props.socket.on('ans',(data) => {
console.log(data.output)
//handleOutput(data.output)
})
}
fireTyping = () => {
ld = this.codeEditor.current.editor.getValue()
//console.log(ld)
if(check) {
console.log(48, this.codeEditor.current.editor.getValue(), check);
this.props.socket.emit("typing", {
text: ld,
});
check = false;
}
console.log(check)
};
onDDChange = (e, data) => {
const selectedVal = languages.filter((v) => v.key == data.value)
this.setState({currentLang : data.value, codeValue: selectedVal[0].template})
}
runCode = () => {
this.props.socket.emit('run', {
code: this.codeEditor.current.editor.getValue(),
lang: this.state.currentLang,
input: ''
})
}
handleOutput = () => {
}
render() {
return (
<div>
<Dropdown
placeholder="Languages"
onChange = {this.onDDChange}
selection
value = {this.state.currentLang}
options={languages}
/>
<AceEditor
style={{
margin: "3rem auto",
width: "80vw",
height: "70vh",
}}
fontSize={18}
ref={this.codeEditor}
mode="c_cpp"
theme="github"
value={this.state.codeValue}
onInput={this.fireTyping}
showPrintMargin={false}
name="UNIQUE_ID_OF_DIV"
editorProps={{ $blockScrolling: true }}
setOptions={{
enableBasicAutocompletion: true,
enableLiveAutocompletion: true,
enableSnippets: true,
}}
/>
<div className="container">
<button
onClick={this.runCode}
style={{
marginLeft: "40rem",
}}
className="large ui teal button"
>
Run
</button>
</div>
</div>
);
}
}
export default App;
BACKEND CODE:
const express = require("express");
const request = require("request");
const app = express();
const http = require("http");
const server = http.createServer(app);
const path = require('path')
const socket = require("socket.io");
const io = socket(server);
const port = process.env.PORT || 4676
app.use(express.static(path.join(__dirname, 'client/build')))
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname + '/client/build/index.html'))
})
io.on("connection", (socket) => {
let previousCode, currentCode;
console.log(socket.id);
socket.on("typing", (data) => {
currentCode = data.text
console.log('typing')
console.log(previousCode === currentCode)
if(previousCode !== currentCode){
console.log(1)
io.emit("typed", data);
previousCode = currentCode;
currentCode = ''
}
});
});
server.listen(port, () => {
console.log("server started at http://localhost:4676");
});
I've spent hours trying to fix this but I couldn't.... Any help would be appreciated ☺️
Let me know if you need code reference, I'll share the repository

Why formData does not work with multiple files?

I'm facing a problem with a React project I'm working on: I'm trying to upload multiple images to a Node Express API. I'm using a formData object and I used the append() method to append the form fields from the component State.
In the Express code I'm using multer, all the attributes in the req.body are there but req.files is empty.
I changed the code to upload a single image also using formData() and it works; the problem seems to be only when I try with multiple files using the formData object. I also tested using a regular form (not react) and that also worked!
I'm wondering if there is something I'm missing when I use formData with a file input with multiple files?
import React, { Component } from "react";
import axios from "axios";
class Form extends Component {
constructor() {
super();
this.state = { images: {} };
}
onChangeImages = e => {
this.setState({ images: e.target.files })
};
onSubmit = e => {
e.preventDefault();
const { images } = this.state;
const formData = new FormData();
formData.append("images", images);
axios
.post("/api/post/create", formData)
.then(res => console.log(res.data))
.catch(err => console.error(err));
};
render() {
return (
<form onSubmit={this.onSubmit}>
<input
onChange={this.onChangeImages}
type="file"
name="images"
multiple
accept="image/png, image/jpeg, image/jpg"
/>
<br />
<input type="submit" value="Send" />
</form>
);
}
}
export default Form;
Express code
const express = require('express');
const router = express.Router();
const multer = require('multer');
const upload = multer({ dest: 'uploads/' });
router.post('/create', upload.array('images', 2), (req, res) => {
console.log(req.files);
console.log(req.body);
res.status(200).json(req.body);
});
module.exports = router;
formData.append("images", images);
You need to append each file in turn. FormData doesn't support a FileList object.
for (let i = 0 ; i < images.length ; i++) {
formData.append("images", images[i]);
}
there are a lot of other alternatives than using the backend. You can use cloudinary or firebase to upload all images and get their urls and let your backend store those URLs in the database.

Resources