Blue
Blue

Reputation: 251

How would I go about uploading a image file using Socket-IO?

I am making a chat app as a project in school, and I am trying to add a onClick that runs a function, that uses socket-io-file-upload, to run a prompt function. From the socket-io-file-upload docs. When this method is called, the user will be prompted to choose a file to upload.

JavaScript:

document.getElementById("file_button").addEventListener("click", instance.prompt, false);
HTML:

<button id="file_button">Upload File</button>

Basically I am not sure how I would go about connected the back-end which is being ran separate on how I would be able to use socket-io, in the react front-end as far as using the file-upload..

Here are the files I have right now related to this component in some way - FYI - Using Styled Components

Front-End :

My Reducer (Maybe relevant) -

import React from "react";
import io from "socket.io-client";
export const CTX = React.createContext();

const initState = {
  selectedChannel: "general",
  socket: io(":3001"),
  user: "RandomUser",
  allChats: {
    general: [''],
    channel2: [{ from: "user1", msg: "hello" }],
  },
};
const reducer = (state, action) => {
  console.log(action);
  switch (action.type) {
    case "SET_CHANNEL_NAME":
      const newChannelName = action.payload;
      return {
        ...state,
        allChats: {
          ...state.allChats,
          [newChannelName]: [{from: "ChatBot", msg: "Welcome to a new chatroom!"}]
        }
      }
    case "CREATE_CHANNEL":
      return {
        ...state,
        allChats: {
          ...state.allChats,
          newChannel: [ {from: "chatbot", msg: "Welcome to a new chatroom! Type away!"}]
        }
      };
    case "SET_USER_NAME":
      return {
        ...state,
        user: action.payload,
      };
    case "SET_SELECTED_CHANNEL":
      return {
        ...state,
        selectedChannel: action.payload,
      };
    case "RECEIVE_MESSAGE":
      const { from, msg, channel } = action.payload;
      return {
        ...state,
        allChats: {
          ...state.allChats,
          [channel]: [...state.allChats[state.selectedChannel], { from, msg }],
        },
      };
    default:
      return state;
  }
};

// const sendChatAction = (value) => {
//     socket.emit('chat message', value);
// }

export const Store = (props) => {
  const [state, dispatch] = React.useReducer(reducer, initState);

  const myDispatch = (type, payload) => {
    if (typeof type === "object" && type !== null) {
      dispatch(type);
    }
    dispatch({ type, payload });
  };

  return (
    <CTX.Provider value={{ state, dispatch: myDispatch }}>
      {props.children}
    </CTX.Provider>
  );
};

ChatBox.js -

import React from "react";
import styled from "styled-components";
import Sidebar from "../Sidebar";
import io from 'socket.io-client'
import UserMessage from "../UserMessage";
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faPlus } from '@fortawesome/free-solid-svg-icons'
import InputAddon from '../InputAddon'



import { CTX } from '../Store'


const ChatBox = () => {
  const [textValue, changeTextValue] = React.useState('');

  const { state, dispatch } = React.useContext(CTX);
  console.log(state.user)
  React.useEffect(() => {
    console.log(state.user)

    state.socket.on('message', function (msg) {
      console.log("chat message recieved")
      dispatch('RECEIVE_MESSAGE', msg);
    })
  }, [])



  const onKeyPressHandler = (e) => {
    if (e.key === 'Enter') {
      e.preventDefault();
      console.log("PRESSED")
      state.socket.emit('sent message', { from: state.user, msg: textValue, channel: state.selectedChannel });
      dispatch('RECEIVE_MESSAGE', { from: state.user, msg: textValue, channel: state.selectedChannel });
      changeTextValue('')
    }
  }

  const onChangeHandler = e => {
    changeTextValue(e.target.value);
  }


  return (

    <Layout>
      <Sidebar />
      <Wrapper>
        <InnerBoxWrapper>
          <InnerBox>
            <UserMessage />
            <InputWrapper>
              <InputAddons id="InputAddon">
                <FontAwesomeIcon icon={faPlus} onClick={InputAddon}></FontAwesomeIcon>
              </InputAddons>
              <input
                label="Send a chat"
                onChange={onChangeHandler}
                value={textValue}
                onKeyPress={onKeyPressHandler}
              />
            </InputWrapper>
          </InnerBox>
        </InnerBoxWrapper>
      </Wrapper>
    </Layout>
  )
}

InputAddon.js -

import React from 'react';

const InputAddon = () => {
    console.log('clicked')
}

export default InputAddon;

BACKEND -

www.js -

/**
 * Module dependencies.
 */

var app = require('../app');
var debug = require('debug')('cryptidbackend:server');
var http = require('http').createServer(app);
const io = require('socket.io')(http);
const siofu = require('socketio-file-upload')
const cors = require('cors');
app.use(cors());
// Socket.io 

io.on('connection', function (socket) {
  const uploader = new siofu(socket);
  uploader.prompt(document.getElementById("InputAddon"))
  uploader.listen(socket)
  socket.on('sent message', function (msg) {
    console.log('message' + ' : ' + JSON.stringify(msg))
    socket.broadcast.emit('message', msg);
  })
})


/**
 * Get port from environment and store in Express.
 */
http.listen(3001, function () {
  console.log('listening on 3001')
})

app.js -

const siofu = require('socketio-file-upload')
const app = express()

const cors = require("cors");
const bodyParser = require("body-parser");
const logger = require("morgan");
const session = require("express-session");
const FileStore = require("session-file-store")(session);
const upload = require("express-fileupload");

app.use(siofu.router)

app.use(upload());
console.log("Server Started!");

app.use(logger("dev"));

app.use(cors());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(
  session({
    resave: false,
    secret: "hello",
    saveUninitialized: true,
    is_logged_in: false,
  })
);

const indexRouter = require("./routes/index");
app.use("/", indexRouter);

const usersRouter = require('./routes/users');
app.use('/users', usersRouter);

module.exports = app;

If you have any questions, or can give me any tips please do I am only about 5 months into my coding career so I still have a lot to learn.

Upvotes: 3

Views: 7585

Answers (4)

iacenter
iacenter

Reputation: 1

Uncaught TypeError: SocketIOFileUpload is not a constructor

import {SocketIOFileUpload} from "socketio-file-upload";

useEffect(() => {
    const siofu = new SocketIOFileUpload(socket);
    // call listen on input and pass the hidden input ref
 }, [socket])

but :

Uncaught TypeError: SocketIOFileUpload is not a constructor

Upvotes: 0

Ayan Banerjee
Ayan Banerjee

Reputation: 1

identify the image file from your destination location then use

public static byte[] readBytesFromFile(File file) throws IOException 
{
       InputStream is = new FileInputStream(file);

       // Get the size of the file
       long length = file.length();

       // You cannot create an array using a long type.
       // It needs to be an int type.
       // Before converting to an int type, check
       // to ensure that file is not larger than Integer.MAX_VALUE.
       if (length > Integer.MAX_VALUE) 
       {
           is.close();
         throw new IOException("Could not completely read file " + file.getName() + " as it is too long (" + length + " bytes, max supported " + Integer.MAX_VALUE + ")");

       }

       // Create the byte array to hold the data
       byte[] bytes = new byte[(int)length];

       // Read in the bytes
       int offset = 0;
       int numRead = 0;
       while (offset < bytes.length && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) 
       {
           offset += numRead;
       }

       // Ensure all the bytes have been read in
       if (offset < bytes.length) 
       {
           is.close();
           throw new IOException("Could not completely read file "   +file.getName());
       }

       // Close the input stream and return bytes
       is.close();
       return bytes;
}

store image data into byte array.

Then, mention your destination location and apply

It will run.

Upvotes: -2

Shubham Khatri
Shubham Khatri

Reputation: 281834

In order to upload file from frontend, what you need to do is to create an instance of socket-file-upload on frontend in a useEffect inside ChatBox component.

Also you need to create a dummy hidden input on which you can simulate click when Upload button is clicked and also the input on which you can listen on

Small snippet code that you need to add

  const fileRef = useRef(null);
  useEffect(() => {
     const siofu = new SocketIOFileUpload(state.socket);
     // call listen on input and pass the hidden input ref
     siofu.listenOnInput(fileRef.current);
  }, [state.socket])

  const InputAddon = () => {
     // Trigger click on fileRef input
     fileRef.current.click();
  }

Full component code with input

import SocketIOFileUpload from 'socketio-file-upload';
const ChatBox = () => {
  const [textValue, changeTextValue] = React.useState('');

  const { state, dispatch } = React.useContext(CTX);
  console.log(state.user)
  React.useEffect(() => {
    console.log(state.user)

    state.socket.on('message', function (msg) {
      console.log("chat message recieved")
      dispatch('RECEIVE_MESSAGE', msg);
    })
  }, [])





  const onKeyPressHandler = (e) => {
    if (e.key === 'Enter') {
      e.preventDefault();
      console.log("PRESSED")
      state.socket.emit('sent message', { from: state.user, msg: textValue, channel: state.selectedChannel });
      dispatch('RECEIVE_MESSAGE', { from: state.user, msg: textValue, channel: state.selectedChannel });
      changeTextValue('')
    }
  }

  const onChangeHandler = e => {
    changeTextValue(e.target.value);
  }
  const fileRef = useRef(null);
  useEffect(() => {
     const siofu = new SocketIOFileUpload(state.socket);
     // call listen on input and pass the hidden input ref
     siofu.listenOnInput(fileRef.current);
  }, [state.socket])

  const InputAddon = () => {
     // Trigger click on fileRef input
     fileRef.current.click();
  }

  return (

    <Layout>
      <Sidebar />
      <Wrapper>
        <InnerBoxWrapper>
          <InnerBox>
            <UserMessage />
            <InputWrapper>
              <InputAddons id="InputAddon">
                <FontAwesomeIcon icon={faPlus} onClick={InputAddon}></FontAwesomeIcon>
              </InputAddons>
               <input
                ref={fileRef}
                label="file-picker"
                type="file"
                style={{display: 'none'}}
              />
              <input
                label="Send a chat"
                onChange={onChangeHandler}
                value={textValue}
                onKeyPress={onKeyPressHandler}
              />
            </InputWrapper>
          </InnerBox>
        </InnerBoxWrapper>
      </Wrapper>
    </Layout>
  )
}

In the backend code you need to create a express server and add sockiofileUpload router to it

var app = require('../app');
var debug = require('debug')('cryptidbackend:server');
const socketio = require('socket.io');

/**
 * Get port from environment and store in Express.
 */
app.listen(3001, function () {
  console.log('listening on 3001')
})



var io = socketio.listen(app);

// Socket.io 

io.sockets.on('connection', function (socket) {
    const uploader = new siofu(socket);
    uploader.listen(socket)
    uploader.dir = "/srv/uploads";
    uploader.listen(socket);

    // Do something when a file is saved:
    uploader.on("saved", function(event){
        console.log(event.file);
    });

    // Error handler:
    uploader.on("error", function(event){
        console.log("Error from uploader", event);
    });
})

app.js

const siofu = require('socketio-file-upload')
const app = express()

const cors = require("cors");
const bodyParser = require("body-parser");
const logger = require("morgan");
const session = require("express-session");
const FileStore = require("session-file-store")(session);

app.use(siofu.router)

console.log("Server Started!");

app.use(logger("dev"));

app.use(cors());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(
  session({
    resave: false,
    secret: "hello",
    saveUninitialized: true,
    is_logged_in: false,
  })
);

const indexRouter = require("./routes/index");
app.use("/", indexRouter);

const usersRouter = require('./routes/users');
app.use('/users', usersRouter);

Upvotes: 5

Y Munawwer
Y Munawwer

Reputation: 126

first of all this part of the code is in backend and in backend there is no document or window.which means document.getElementById wont work here.

 io.on('connection', function (socket) {
  const uploader = new siofu(socket);
  uploader.prompt(document.getElementById("InputAddon"))
  uploader.listen(socket)
  socket.on('sent message', function (msg) {
    console.log('message' + ' : ' + JSON.stringify(msg))
    socket.broadcast.emit('message', msg);
  })
})

then there is no upload file location/dir is define.

you can see the example

express-fileupload

app.use(fileUpload({
    useTempFiles : true,
    tempFileDir : '/tmp/'
}));

socketio-file-upload

io.on("connection", function(socket){
    var uploader = new siofu();
    uploader.dir = "/path/to/save/uploads";  ***//upload directory***
    uploader.listen(socket);
});

so what i will suggest you is go throught the link mention below and update your code.

https://www.npmjs.com/package/express-fileupload

https://github.com/sffc/socketio-file-upload

Upvotes: 0

Related Questions