m-expunged / guacamole-sharp

C# replacement of the Apache Guacamole server-side Java servlet.
Apache License 2.0
26 stars 3 forks source link

File transfer via SFTP on a SSH connection #22

Closed darksody closed 1 year ago

darksody commented 1 year ago

Hello,

Implementing your solution, i'm trying to transfer files via SFTP.

Having an SSH connection with enable-SFTP set to true, and configured a SFTP directory with permissions on it, i do the following:

I get the filesystem object via this event:

client.onfilesystem = (object, name) => { console.log(name); console.log(object); filesystem = object; }

Now, I have a download method for a file:

`function DownloadFileSFTP(path, filesystem) { console.log('Requesting download file: ' + path); filesystem.requestInputStream(path, function downloadStreamReceived(stream, mimetype) { var filename = path.match(/(.[\/])?(.)/)[2]; console.log('Parsed file name: ' + filename);

// Begin file download
var blob_reader = new Guacamole.BlobReader(stream, mimetype);

// Update progress as data is received
blob_reader.onprogress = function onprogress() {
  var progressLength = blob_reader.getLength();
  console.log('progress: ' + progressLength);
  // Signal server that data was received
  stream.sendAck("Received", Guacamole.Status.Code.SUCCESS);
};

// Save blob and close stream when complete
blob_reader.onend = function onend() {
  // Save blob
  var blob = blob_reader.getBlob();
  console.log('blob:');
  console.log(blob);
  downloadBlob(blob, filename);
};

// Signal server that data is ready to be received
stream.sendAck("Ready", Guacamole.Status.Code.SUCCESS);

}); }`

Where the "file" parameter is actually the myform.files[0] object. This works perfectly, i'm downloading files of a few MB without an issue.

File upload on the other hand, here's the challenge that I hope you can help me solve. This is my method:

`function UploadFileSFTP(path, filesystem, file, onprogress) { console.log('Uploading file ' + file.name + ' to ' + path); console.log('file type: ' + file.type);

let stream = filesystem.createOutputStream(file.type, path);

stream.onack = function beginUpload(status) { if (status.isError()) { console.log('Guacamole error on stream'); console.log(status); return; }

var blob_writer = new Guacamole.BlobWriter(stream, file.type);
blob_writer.oncomplete = function uploadComplete() {
  console.log('upload complete');
  blob_writer.sendEnd();
};

blob_writer.onack = function packagereceived(status) {
  console.log('ack status: ');
  console.log(status);
};

blob_writer.onerror = function uploadError() {
  console.log('upload error');
};

if (file.size > 0) {
  console.log('uploading file of size: ' + file.size);
  var slice_size = 1024;
  var sent = 0;

  while (sent < file.size) {

    //get slices of slice_size, or what's left of the last chunk

    //get a slice of the file
    var data = file.slice(sent, sent + slice_size);
    blob_writer.sendBlob(data);

    //increment sent bytes
    sent += tosend;
    console.log(sent);
  }
} else {
  console.log('error');
}

} }`

If i use slices equal to 64, it works, but ONLY on very small files (not even 1kb worth of files). If i send a file as a whole, doesn't work. If i use the slice larger than 64, basically nothing happens. The file gets created (thanks to the createOutputStream method) with 0 bytes, and nothing gets written. I get no ACK back from the stream.

I do suspect it has something to do with the frame buffer you're using in the backend websocket, but i can't figure this out.

darksody commented 1 year ago

Closing the issue, I was using an old version where frames were being parsed individually.

Updated working javascript in case anybody else needs it:

`function UploadFileSFTP(path, filesystem, file, onprogress) { console.log('Uploading file ' + file.name + ' to ' + path); console.log('file type: ' + file.type); console.log('file size: ' + file.size);

let stream = filesystem.createOutputStream(file.type, path);

stream.onack = function beginUpload(status) { if (status.isError()) { console.log('Guacamole error on stream'); console.log(status); return; }

var offset = 0;
var chunkSize = 1024 * 1024; // 1MB

function uploadNextChunk() {
  var reader = new FileReader();
  var chunk = file.slice(offset, offset + chunkSize);

  reader.onload = function (event) {
    // Get the chunk content
    var chunkContent = event.target.result;
    console.log(chunkContent.byteLength);
    // Convert the chunk content to a Blob
    var chunkBlob = new Blob([chunkContent]);

    // Write the chunk to the output stream
    var writer = new Guacamole.BlobWriter(stream);
    writer.oncomplete = function () {
      offset += chunkSize;

      // Check if there are more chunks to upload
      if (offset < file.size) {
        // Upload the next chunk
        uploadNextChunk();
      } else {
        // File upload complete
        console.log('File uploaded successfully');
      }
    };
    writer.sendBlob(chunkBlob);
  };

  reader.readAsArrayBuffer(chunk);
}

// Start uploading chunks
uploadNextChunk();

} }`