F# FTP Fails where C# Succeeds

我怕爱的太早我们不能终老 提交于 2020-01-14 04:54:06

问题


I've an application written in C# (.Net 3.5) with this code.

using System;
using System.Net;

string strURI = String.Format("ftp://x{0}ftp/%2F'{1}'", parm1, parm2);
FtpWebRequest ftp = (FtpWebRequest)FtpWebRequest.Create(strURI);
ftp.Proxy = null;
ftp.KeepAlive = true;
ftp.UsePassive = false;
ftp.UseBinary = false;
ftp.Credentials = new NetworkCredential("uid", "pass");
ftp.Method = WebRequestMethods.Ftp.DownloadFile;
FtpWebResponse response = (FtpWebResponse)ftp.GetResponse();
...

I've translated this into F# (.Net 4.0) to use in my application.

open System.Net

let uri = sprintf "ftp://x%sftp/%%2F'%s'" parm1 parm2
let ftp = FtpWebRequest.Create(uri) :?> FtpWebRequest
ftp.Credentials <- new NetworkCredential("uid", "pass")
ftp.Method <- WebRequestMethods.Ftp.DownloadFile
ftp.Proxy <- null
let response = ftp.GetResponse() :?> FtpWebResponse
...

At this point, FSI complains.

System.Net.WebException: The remote server returned an error: (550) File unavailable (e.g. file not found, no access).

Yet the C# application runs and successfully downloads the file. What am I missing in F# (besides the properties that aren't there in 4.0, i.e. KeepAlive, UsePassive, and UseBinary)?


回答1:


The code may have errors, I don't have F# compiler right now.

open System
open System.Reflection
open System.Net

let switch_to_legacy_mode _ =
  let wtype = typeof<FtpWebRequest>
  let mfield = wtype.GetField("m_MethodInfo", BindingFlags.NonPublic ||| BindingFlags.Instance)
  let mtype = mfield.FieldType
  let knfield = mtype.GetField("KnownMethodInfo", BindingFlags.Static ||| BindingFlags.NonPublic)
  let knarr = knfield.GetValue(null) :?> Array
  let flags = mtype.GetField("Flags", BindingFlags.NonPublic ||| BindingFlags.Instance)
  let flag_val = 0x100
  for f in knarr do
    let mutable ff = flags.GetValue(f) :?> int
    ff <- ff ||| flag_val
    flags.SetValue(f, ff)

let uri = sprintf "ftp://x%sftp/%%2F'%s'" parm1 parm2
do switch_to_legacy_mode () // Call it once before making first FTP request
let ftp = FtpWebRequest.Create(uri) :?> FtpWebRequest
ftp.Credentials <- new NetworkCredential("uid", "pass")
ftp.Method <- WebRequestMethods.Ftp.DownloadFile
ftp.Proxy <- null
let response = ftp.GetResponse() :?> FtpWebResponse

Source: http://support.microsoft.com/kb/2134299

The cause of this issue is due to a behavior change in the System.Net.FtpWebRequest class in .Net Framework 4. There has been a change made to the System.Net.FtpWebRequest class from .Net Framework 3.5 to .Net Framework 4 to streamline the use of the CWD protocol commands. The new implementation of the System.Net.FtpWebRequest class prevents the send of extra CWD commands before issuing the actual command which the user requested and instead directly sends the requested command. For fully RFC compliant FTP servers, this should not be an issue, however for non-fully RFC compliant servers, you will see these types of errors.




回答2:


Try this instead:

open System
open System.Net

let parm1 = "test"
let parm2 = "othertest"

let uri = String.Format("ftp://x{0}ftp/%2F'{1}'", parm1, parm2)
let ftp = FtpWebRequest.Create(uri) :?> FtpWebRequest;;

I had to add a dummy parm1 and parm2 but I'm assuming you've got parm1 and parm2 defined in your actual code. I'd guess that whatever the issue is, it lies in the uri string. That is, I think you'll find the problem by comparing uri to the known to be good uri string.



来源:https://stackoverflow.com/questions/15644969/f-ftp-fails-where-c-sharp-succeeds

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!