locked
Null reference from FtpWebRequest.GetRequestStream when targeting .NET 4.0

    Question

  • Hi there. 

    I am having an issue when uploading files to an FTP server. 

    The code I am using is part of a class library built to contain reusable classes which targets .NET 2.0 and is shared across many projects.

    This code works without any problems when an application using this code targets .NET 2.0 but now I have a project where I need to call into this code and it targets .NET 4.0

    When calling the code from a project which targets .NET 4.0, the upload fails on the GetRequestStream() method on the FtpWebRequest object with a null reference exception. I have verified this by calling this class library from a simple console application and receive the same exception.

    It is not possible to change the calling application back to support .NET 2.0 because of the amount of dependencies contained within the project (which also target .NET 4.0)

    Here is the ftp upload code:

     

    public static class FtpUtils
     {
      public static void UploadFile(string physicalFilePath, string ftpFilePath)
      {
       UploadFile(physicalFilePath, ftpFilePath, null, false);
      }
    
      public static void UploadFile(string physicalFilePath, string ftpFilePath, NetworkCredential ftpCredentials)
      {
       UploadFile(physicalFilePath, ftpFilePath, ftpCredentials, true);
      }
    
      public static void UploadFile(string physicalFilePath, string ftpFilePath, NetworkCredential ftpCredentials, bool checkTargetExists)
      {
       FtpWebRequest request = CreateFtpRequest(ftpFilePath, checkTargetExists ? CheckIfFileIsUploaded(ftpFilePath, ftpCredentials) : false, ftpCredentials);
    
       if (request != null)
       {
        // load the log file
        byte[] buffer;
        using (FileStream stream = File.OpenRead(physicalFilePath))
        {
         Trace.WriteLine("Reading {0} to a stream for upload");
         buffer = new byte[stream.Length];
         stream.Read(buffer, 0, buffer.Length);
         stream.Close();
        }
    
        // upload the file
        if (buffer != null && buffer.Length > 0)
        {
         Trace.WriteLine("Starting to upload file");
         using (Stream reqStream = request.GetRequestStream())
         {
          reqStream.Write(buffer, 0, buffer.Length);
          reqStream.Close();
          reqStream.Dispose();
         }
        }
       }
      }
    
      private static FtpWebRequest CreateFtpRequest(string uploadPath, bool targetExists, NetworkCredential ftpCredentials)
      {
       FtpWebRequest request = (FtpWebRequest)FtpWebRequest.Create(uploadPath);
    
       // if the file does not exist, upload the file otherwise append to the existing file
       request.Method = (targetExists) ? WebRequestMethods.Ftp.AppendFile : WebRequestMethods.Ftp.UploadFile;
    
       // specify login credentials
       request.Credentials = ftpCredentials;
    
       // use active mode
       request.UsePassive = false;
    
       // uploading a binary file
       request.UseBinary = true;
    
       // close the connection to the server once we're done
       request.KeepAlive = false;
    
       return request;
      }
    
      private static bool CheckIfFileIsUploaded(string remotePath, NetworkCredential ftpCredentials)
      {
       FtpWebRequest request = (FtpWebRequest)WebRequest.Create(remotePath);
       request.Method = WebRequestMethods.Ftp.GetFileSize;
    
       // specify login credentials
       request.Credentials = ftpCredentials;
    
       // use active mode
       request.UsePassive = false;
    
       try
       {
        FtpWebResponse response = (FtpWebResponse)request.GetResponse();
    
        return (response.ContentLength > 0);
       }
       catch
       {
        // file may not exist
        return false;
       }
      }
    

     

    I am calling the code as follows:

     

    FtpUtils.UploadFile(
     "local file path", 
     "ftp://someserver.com/filename of file", 
     new NetworkCredential
     {
      Username = "auser",
      Password = "apass"
     });
    
    

    Further information.

    The inner exception is as follows:

    Object reference not set to an instance of an object.

       at System.Net.NetworkCredential.InternalGetDomainUserName()

       at System.Net.FtpControlStream.BuildCommandsList(WebRequest req)

       at System.Net.CommandStream.SubmitRequest(WebRequest request, Boolean async,

    Boolean readInitalResponseOnConnect)

       at System.Net.FtpWebRequest.TimedSubmitRequestHelper(Boolean async)

       at System.Net.FtpWebRequest.SubmitRequest(Boolean async)

    • Moved by Leo Liu - MSFT Monday, May 30, 2011 4:58 AM Moved for better support. (From:Visual C# General)
    Thursday, May 26, 2011 1:59 PM

Answers

  • The issue is here:

    new NetworkCredential
    {
      Username = "auser",
      Password = "apass"
    });

    Replace that with:

    new NetworkCredential(
      userName: "auser",
      password: "apass"
     );

    or

    new NetworkCredential(
      "auser", "apass"
     );

    The reason is, your existing code calls the default NetworkCredential constructor, and then sets properties on the credential instance.

    However, the default NetworkCredential constructor does not set a value for "Domain", which all other code, including framework functions, assumes is null (indeed, you can't set it explicitly to null - it will simply be set to String.Empty). This is a bug.

    Another solution is therefore:

    new NetworkCredential
    {
      Username = "auser",
      Password = "apass",
      Domain = "",
    });
     

    //Edit:

    I've since written a blog post on my findings for those interested: http://mattmitchell.com.au/zebras-in-system-net-networkcredential/



     



    • Marked as answer by kevinjp Monday, October 17, 2011 8:13 AM
    • Edited by MattMitchell Sunday, July 15, 2012 5:27 PM
    Monday, October 17, 2011 5:15 AM

All replies

  • I am moving your thread into the .NET Framework Networking and Communication Forum for specialized support. Thanks.
    Leo Liu [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Monday, May 30, 2011 4:58 AM
  • The issue is here:

    new NetworkCredential
    {
      Username = "auser",
      Password = "apass"
    });

    Replace that with:

    new NetworkCredential(
      userName: "auser",
      password: "apass"
     );

    or

    new NetworkCredential(
      "auser", "apass"
     );

    The reason is, your existing code calls the default NetworkCredential constructor, and then sets properties on the credential instance.

    However, the default NetworkCredential constructor does not set a value for "Domain", which all other code, including framework functions, assumes is null (indeed, you can't set it explicitly to null - it will simply be set to String.Empty). This is a bug.

    Another solution is therefore:

    new NetworkCredential
    {
      Username = "auser",
      Password = "apass",
      Domain = "",
    });
     

    //Edit:

    I've since written a blog post on my findings for those interested: http://mattmitchell.com.au/zebras-in-system-net-networkcredential/



     



    • Marked as answer by kevinjp Monday, October 17, 2011 8:13 AM
    • Edited by MattMitchell Sunday, July 15, 2012 5:27 PM
    Monday, October 17, 2011 5:15 AM
  • I have already managed to figure this one (entirely by accident!). Marking your post as the answer Matt. :)
    Monday, October 17, 2011 8:13 AM