javascriptnode.jstypescriptsftpssh2-sftp

Upload inputted file to sftp server


i am trying to send a file i've uploaded from a javacsript input form to my sftp server. i've decoupled the react app and the nodejs server, so read the file as a URL and send it to the server that then calls the put function of the ssh2-sftp-client. The problem is that when it tries to put it doesn't work and returns status code 500 as should. i've tried sending directly the inputFile to the server side but it won't pass it as if i console log the passed file in the server it returns an empty array: {}. how can i solve this issue? thanks for the help.

this is the client side:

async function handleDockerInput(e) {
    const inputFile = e.target.files[0];

    try {
      const loginResponse = await authService.logIn(user);
      console.log('Login Response: ', loginResponse);

      if (inputFile) {
        const reader = new FileReader();

        reader.readAsDataURL(inputFile);

        reader.onload = async (event) => {
          const dataUrl = event.target.result;

          const body = JSON.stringify({
            connect: SFTPConfig,
            filepath: dataUrl,
            remoteFilePath: '/'
          });

          const response = await dockerService.putObject(body);

          console.log(response);
        };
      } else {
        alert('Please upload a file');
      }
    } catch (error) {
      console.error('Failed to login: ', error);
    }
  }

here is the dokerService.putObect function:

async putObject(body) {
    const response = await axios.post('/docker/upload', body);
    return response;
  }

here is the server side function that is called:

public async upload(req: Request, res: Response): Promise<void> {
    const dataUrl = req.body.filepath;

    const remoteFilePath: string = req.body.remoteFilePath; 
    const sftp = new Client();
    const { host, port, username, password } = req.body.connect;

    const sftpConfig: SftpConfig = {
      host: host,
      port: port,
      username: username,
      password: password,
    };

    try {
      await sftp.connect(sftpConfig);
      const pathExists = await sftp.stat(remoteFilePath);

      if(pathExists) {
        try {
          await sftp.put(dataUrl, remoteFilePath);
          console.log('File uploaded successfully');
          res.status(200).send('File uploaded successfully'); // Example error handling
        } catch (error) {
          res.status(500).send('Error uploading file to SFTP here'); // Example error handling
        }
      } else {
        console.log('Inserted path doesn\'t exist');
        res.status(500).send('Inserted path doesn\'t exist'); // Example error handling
      }
    } catch (error) {
      console.log(error, 'catch error');
      res.status(500).send('Error uploading file to SFTP'); // Example error handling
    } finally {
      sftp.end(); // Ensure the connection is closed after operation
    }
  }

Solution

  • The problem is probably you're not passing correct input to sftp.put method:

    await sftp.put(dataUrl, remoteFilePath);
    

    As per docs, it takes a string, buffer or a readable stream:

    put(src, remotePath, options) ==> string

    src: string | buffer | readable stream. Data source for data to copy to the remote server.

    https://www.npmjs.com/package/ssh2-sftp-client#putsrc-remotepath-options--string

    You're passing it a base64 string (dataUrl), which is not an acceptable input (base64 is a string, but here it's expected to be a local file path, so it probably fails here...)

    So, try converting base64 string to image with Buffer, and then pass that.

    Try this:

    // convert base64 to file - png image example, or set your type
    const base64ToBuffer = Buffer.from(dataUrl.replace(/^data:image\/png;base64,/, ''), 'base64');
    // or
    // to remove any content type from base64 string
    // const base64ToBuffer = Buffer.from(dataUrl.split(',')[1], 'base64');
    
    // pass buffer
    await sftp.put(base64ToBuffer, remoteFilePath);
    

    Also, actual filename is required, not just path:

    await sftp.put(base64ToBuffer, remoteFilePath + 'file.ext');