Node-SMB / marsaud-smb2

SMB2 Client
53 stars 46 forks source link

Error STATUS_FILE_CLOSED after pipe stream created with createReadStream #82

Open julien13630 opened 2 years ago

julien13630 commented 2 years ago

Hi,

I have an error when i'm reading list of file using readstream. I have a loop on all file of a dir. For each file I create a read stream that I pipe to a csv parser streamwritter (https://www.npmjs.com/package/csv-parser). File is read well but when at the end of loop an error STATUS_FILE_CLOSED occures :

[...]

for (let file of files) {
            try {
                if(file.isDirectory())
                {
                    continue;
                }
                var rs = await smb2Client.createReadStream('dummy\\path\\test\\' + file.name,);
                rows.push(...await manageCsvFile(rs));
            } catch (e) {
                console.log('error on file' + file.name, e)
            }
}
[...]

--------

async function manageCsvFile(rs) {
    let rows = [];
    var fd = rs.pipe(csv({
        separator: ';'
    }));
    var end = new Promise(function(resolve, reject) {
        fd.on('data', (row) => {
            rows.push(row);
        })
        fd.on('end', () => {
            console.log('CSV file successfully processed');
        })
        fd.on('close', () => {
            console.log('Stream has been destroyed and file has been closed');
            resolve();
        });
    });
    await end;
    return rows;
}

----ERROR Message

"errorType": "Error",
  "errorMessage": "STATUS_FILE_CLOSED (0xC0000128) : An I/O request other than close and several other special case operations was attempted using a file object that had already been closed.",
  "trace": [
    "Error: STATUS_FILE_CLOSED (0xC0000128) : An I/O request other than close and several other special case operations was attempted using a file object that had already been closed.",
    "    at SMB2Forge.request (/var/task/node_modules/@marsaud/smb2/lib/tools/smb2-forge.js:22:15)",
    "    at Readable.stream._destroy (/var/task/node_modules/@marsaud/smb2/lib/api/createReadStream.js:42:9)",
    "    at Readable.destroy (internal/streams/destroy.js:39:8)",
    "    at endReadableNT (internal/streams/readable.js:1350:16)",
    "    at processTicksAndRejections (internal/process/task_queues.js:82:21)"
  ]

Can you help me on this issue ? Best regards

brunocw-cit commented 2 years ago

Also affecting me. Although it looks like the file is being read thoroughly

mbrowng commented 2 years ago

The same is happening to me, and as @brunocw-cit said, the file is being read.

Any help?

brunocw-cit commented 2 years ago

hey @mbrowng i know its not ideal (if you're reading a large file) but what worked for me was using 'await smb2client.readFile' instead of 'createReadStream' to read my file all at once instead of streaming it.

Akxe commented 1 year ago

So!... I have found out how to hide this error while (hopefully) not destroying anything else!

const stream = await smb2Client.createReadStream(path);
res.contentType(extname(path));
stream.pipe(res).once('drain', () => stream._destroy = () => { });

I know this looks scary, but the stream gets closed twice. Replacing _destroy method that calls the second close event solved the issue. To validate the claim, you can also look for "close" and "finished" events, where you will see that stream.destroyed is true.

lschipper-VW commented 1 year ago

Do we have an update on this? Seems to be happening when I upgrade to node ~>=16

jackaperkins commented 1 year ago

@Akxe i tried your fix, but i'm still seeing the error. the stream does seem to work fine despite this.

If anyone else sees this or has a solution for streaming files over SMB in node in 2023 I'd be much obliged!

my demo code which returns the file's contents over simple http server


import SMB2 from '@marsaud/smb2'
import http from 'node:http'

let smb2Client = new SMB2({
  share: '\\\\localhost\\public',
  domain: 'somedomain',
  username: 'myuser',
  password: 'mypass'
})

let server = http.createServer(async (req, res) => {
  console.log(`Request for ${req.method} - ${req.url}`)
  let filePath = 'folder/test_file'

  let stream = await smb2Client.createReadStream(filePath)

  res.write('some value from SMB:')
  stream.pipe(res).once('drain', () => stream._destroy = () => { console.log('closing a second time?') })
})

server.listen(3000, () => console.log('listening on 3000'))
siddiq-rehman commented 5 months ago

It worked for me, By using SmbClient.Open to get the fd descriptor.

Then using the file descriptor for opening the file for stream and chosing option "autoClose"

Then once the file is read, closing the fd by yourself