SFTP FileServer LocalFileSystem SetContent event

0 votes
asked Aug 23 by Yahia2020 (230 points)

Hi,

in a sanity test I have implemented GetContentSurrogate event handler and SetContentSurrogate event handler.

The GetContentSurrogate looks like this:

private void VNotifier_GetContentSurrogate(object sender, GetContentEventArgs e)
{
    if ((e.ContentParameters.AccessType.HasFlag(NodeContentAccess.Write)) && (e.Node.Exists()))
    {
        e.Node.Context = @"E:\Testdaten\TEMP\" + System.Guid.NewGuid().ToString("N");
        var vStream = new System.IO.FileStream(e.Node.Context as string, System.IO.FileMode.CreateNew, System.IO.FileAccess.ReadWrite, System.IO.FileShare.None);
        e.ResultContent =  NodeContent.CreateDelayedWriteContent(vStream);
        e.MarkAsHandled();
    }
}

This event handler works totally fine.
BUT the event handler for SetContentSurrogate gets NEVER called (verified in debugger...).

Am I doing something wrong? Or is there a bug? Or does LocalFileSystem not support SetContentSurrogate event?

Thanks in advance.

Best Regards
Yahia

Applies to: File Server, Rebex SFTP

1 Answer

0 votes
answered Aug 25 by Lukas Matyska (57,550 points)

Thank you for reporting this. You did everything correctly.

The SetContentSurrogate event is supported by LocalFileSystem.
However, the SetContentSurrogate event is not fired if a custom NodeContent is set using the GetContentSurrogate event.

The GetContentSurrogate event was primarily introduced to easily override content reading rather than writing. Although the GetContentSurrogate event can be used for writing, it is suggested to implement a custom file system provider for such task. You can get inspired by this sample.

We will discuss whether this behavior should be changed so the SetContentSurrogate event is fired for custom NodeContent as well. I will let you know here what is our decision.

Possible workaround:
The SetContentSurrogate event is fired when the underlying stream is being closed. You can easily be notified when this happen if you implement such stream. For example like this:

private class CloseNotifyingFileStream : System.IO.FileStream
{
    /// <summary>
    /// Raised before the stream is disposed.
    /// </summary>
    public EventHandler<EventArgs> Closing;

    /// <summary>
    /// Raised after the stream is closed.
    /// </summary>
    public EventHandler<EventArgs> Closed;

    public CloseNotifyingFileStream(string path, FileMode mode, FileAccess access, FileShare share) : base(path, mode, access, share)
    {
    }

    protected override void Dispose(bool disposing)
    {
        try
        {
            var closing = Closing;
            if (closing != null) closing(this, EventArgs.Empty);
        }
        finally
        {
            base.Dispose(disposing);
        }

        var closed = Closed;
        if (closed != null) closed(this, EventArgs.Empty);
    }
}

Then use it to handle stream close action. For example like this:

provider.GetFileSystemNotifier().GetContentSurrogate += (s, e) =>
{
    if ((e.ContentParameters.AccessType.HasFlag(NodeContentAccess.Write)) && (e.Node.Exists()))
    {
        e.Node.Context = @"E:\Testdaten\TEMP\" + System.Guid.NewGuid().ToString("N");
        var vStream = new CloseNotifyingFileStream(e.Node.Context as string, System.IO.FileMode.Create, System.IO.FileAccess.ReadWrite, System.IO.FileShare.None);
        var content = NodeContent.CreateDelayedWriteContent(vStream);

        vStream.Closing += (_, __) =>
        {
            Console.WriteLine("Closing {0} (length={1}).", vStream.Name, vStream.Length);

            // inform original node about the change => invokes the SetContentSurrogate event
            e.Node.SetContent(content);
        };

        vStream.Closed += (_, __) =>
        {
            Console.WriteLine("Closed {0}.", vStream.Name);

            // we don't need the file anymore
            File.Delete(vStream.Name);
        };

        e.ResultContent = content;
        e.MarkAsHandled();
    }
};
commented Aug 25 by Yahia2020 (230 points)
Thank you very much...

One question though: would this custom-stream-based workaround still deliver a correct value for WasStreamClosedForcefully ?

Thanks in advance.
commented Aug 25 by Lukas Matyska (57,550 points)
Yes, it still should deliver the correct WasStreamClosedForcefully value.

If you for example do the following in the CloseNotifyingFileStream, the SetContentSurrogate event will be fired with WasStreamClosedForcefully=true.

    public override void Write(byte[] array, int offset, int count)
    {
        throw new InvalidOperationException("Error during write.");
    }
commented Aug 25 by Yahia2020 (230 points)
I tried something similar what you describe (throw new FileSystemException) and WasStreamClosedForcefully was false unfortunately :-(
commented Aug 26 by Lukas Matyska (57,550 points)
I have tried to throw FileSystemException as well and I still get the correct value.

Can you please send us the code fragment to reproduce the behavior?
Probably all provider.GetFileSystemNotifier() handlers + your implementation fo the CloseNotifyingFileStream class.

It seems that you are doing something more (somewhere else), which changes the behavior.

You can either post it here or send it to support@rebex.net.
...