浏览代码

Bug 1738: Stream interface in .NET assembly (upload)

https://winscp.net/tracker/1738
(cherry picked from commit 60c2cab3e0c2edda07905bf12fdce923908516f4)

Source commit: 161b741ded5edbb450bf5a9a2581b5cb6ce5c12f
Martin Prikryl 5 年之前
父节点
当前提交
311e484008
共有 3 个文件被更改,包括 77 次插入3 次删除
  1. 40 0
      dotnet/Session.cs
  2. 3 1
      dotnet/internal/ConsoleCommStruct.cs
  3. 34 2
      dotnet/internal/ExeSessionProcess.cs

+ 40 - 0
dotnet/Session.cs

@@ -688,6 +688,14 @@ namespace WinSCP
         public TransferOperationResult PutFiles(string localPath, string remotePath, bool remove = false, TransferOptions options = null)
         {
             using (Logger.CreateCallstackAndLock())
+            {
+                return DoPutFiles(localPath, remotePath, remove, options);
+            }
+        }
+
+        public TransferOperationResult DoPutFiles(string localPath, string remotePath, bool remove, TransferOptions options)
+        {
+            using (Logger.CreateCallstack())
             {
                 if (options == null)
                 {
@@ -800,6 +808,38 @@ namespace WinSCP
             }
         }
 
+        public void PutFile(Stream stream, string remoteFilePath, TransferOptions options = null)
+        {
+            using (Logger.CreateCallstackAndLock())
+            {
+                if (stream == null)
+                {
+                    throw Logger.WriteException(new ArgumentNullException(nameof(stream)));
+                }
+                if (remoteFilePath == null)
+                {
+                    throw Logger.WriteException(new ArgumentNullException(nameof(remoteFilePath)));
+                }
+                if (_process.StdIn != null)
+                {
+                    throw Logger.WriteException(new InvalidOperationException("Already uploading from a stream"));
+                }
+                _process.StdIn = stream;
+                try
+                {
+                    remoteFilePath = RemotePath.EscapeFileMask(remoteFilePath);
+                    TransferOperationResult operationResult = DoPutFiles("-", remoteFilePath, false, options);
+                    operationResult.Check();
+                    // Assert that any transfer took place at all
+                    GetOnlyFileOperation(operationResult.Transfers);
+                }
+                finally
+                {
+                    _process.StdIn = null;
+                }
+            }
+        }
+
         internal TransferOperationResult PutEntryToDirectory(string localFilePath, string remoteDirectory, bool remove = false, TransferOptions options = null)
         {
             using (Logger.CreateCallstack())

+ 3 - 1
dotnet/internal/ConsoleCommStruct.cs

@@ -4,7 +4,7 @@ using Microsoft.Win32.SafeHandles;
 
 namespace WinSCP
 {
-    public enum ConsoleEvent { None, Print, Input, Choice, Title, Init, Progress, TransferOut }
+    public enum ConsoleEvent { None, Print, Input, Choice, Title, Init, Progress, TransferOut, TransferIn }
 
     [StructLayout(LayoutKind.Sequential)]
     internal class ConsoleInitEventStruct
@@ -182,6 +182,8 @@ namespace WinSCP
 
         public ConsoleTransferEventStruct TransferOutEvent { get { return UnmarshalPayload<ConsoleTransferEventStruct>(ConsoleEvent.TransferOut); } }
 
+        public ConsoleTransferEventStruct TransferInEvent { get { return UnmarshalPayload<ConsoleTransferEventStruct>(ConsoleEvent.TransferIn); } }
+
         private T UnmarshalPayload<T>(ConsoleEvent e)
         {
             CheckNotDisposed();

+ 34 - 2
dotnet/internal/ExeSessionProcess.cs

@@ -20,6 +20,7 @@ namespace WinSCP
         public bool HasExited { get { return _process.HasExited; } }
         public int ExitCode { get { return _process.ExitCode; } }
         public PipeStream StdOut { get; set; }
+        public Stream StdIn { get; set; }
 
         public static ExeSessionProcess CreateForSession(Session session)
         {
@@ -99,7 +100,7 @@ namespace WinSCP
                     string.Format(CultureInfo.InvariantCulture, "/dotnet={0} ", assemblyVersionStr);
 
                 string arguments =
-                    xmlLogSwitch + "/nointeractiveinput /stdout " + assemblyVersionSwitch +
+                    xmlLogSwitch + "/nointeractiveinput /stdout /stdin " + assemblyVersionSwitch +
                     configSwitch + logSwitch + logLevelSwitch + _session.AdditionalExecutableArguments;
 
                 Tools.AddRawParameters(ref arguments, _session.RawConfiguration, "/rawconfig", false);
@@ -326,6 +327,10 @@ namespace WinSCP
                             ProcessTransferOutEvent(commStruct.TransferOutEvent);
                             break;
 
+                        case ConsoleEvent.TransferIn:
+                            ProcessTransferInEvent(commStruct.TransferInEvent);
+                            break;
+
                         default:
                             throw _logger.WriteException(new NotImplementedException());
                     }
@@ -468,7 +473,8 @@ namespace WinSCP
             using (_logger.CreateCallstack())
             {
                 if (!e.UseStdErr ||
-                    (e.BinaryOutput != ConsoleInitEventStruct.StdInOut.Binary))
+                    (e.BinaryOutput != ConsoleInitEventStruct.StdInOut.Binary) ||
+                    (e.BinaryInput != ConsoleInitEventStruct.StdInOut.Binary))
                 {
                     throw _logger.WriteException(new InvalidOperationException("Unexpected console interface options"));
                 }
@@ -555,6 +561,32 @@ namespace WinSCP
             }
         }
 
+        private void ProcessTransferInEvent(ConsoleTransferEventStruct e)
+        {
+            using (_logger.CreateCallstack())
+            {
+                _logger.WriteLine("Len [{0}]", e.Len);
+
+                if (StdIn == null)
+                {
+                    throw _logger.WriteException(new InvalidOperationException("Unexpected data request"));
+                }
+                try
+                {
+                    int len = (int)e.Len;
+                    len = StdIn.Read(e.Data, 0, len);
+                    _logger.WriteLine("{0} bytes read", len);
+                    e.Len = (UIntPtr)len;
+                }
+                catch (Exception ex)
+                {
+                    _logger.WriteLine("Error reading data stream");
+                    _logger.WriteException(ex);
+                    e.Error = true;
+                }
+            }
+        }
+
         private void InitializeConsole()
         {
             using (_logger.CreateCallstack())