使用C#通过FTP上传编码文件

13

以下代码适用于上传文本文件,但无法完整上传JPEG文件(文件名正确,但图像损坏):

private void up(string sourceFile, string targetFile)
{
    try
    {
        string ftpServerIP = ConfigurationManager.AppSettings["ftpIP"];
        string ftpUserID = ConfigurationManager.AppSettings["ftpUser"];
        string ftpPassword = ConfigurationManager.AppSettings["ftpPass"];

        //string ftpURI = "";
        string filename = "ftp://" + ftpServerIP + "//" + targetFile;
        FtpWebRequest ftpReq = (FtpWebRequest)WebRequest.Create(filename);
        ftpReq.Method = WebRequestMethods.Ftp.UploadFile;
        ftpReq.Credentials = new NetworkCredential(ftpUserID, ftpPassword);

        StreamReader stream = new StreamReader(sourceFile);
        Byte[] b = System.Text.Encoding.UTF8.GetBytes(stream.ReadToEnd());
        stream.Close();

        ftpReq.ContentLength = b.Length;
        Stream s = ftpReq.GetRequestStream();
        s.Write(b, 0, b.Length);
        s.Close();

        System.Net.FtpWebResponse ftpResp = (FtpWebResponse)ftpReq.GetResponse();
        MessageBox.Show(ftpResp.StatusDescription);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.ToString());
    }
}

我有另一种可以上载文件的解决方案:

private void Upload(string sourceFile, string targetFile)
{
    string ftpUserID;
    string ftpPassword;
    string ftpServerIP;
    ftpServerIP = ConfigurationManager.AppSettings["ftpIP"];
    ftpUserID = ConfigurationManager.AppSettings["ftpUser"];
    ftpPassword = ConfigurationManager.AppSettings["ftpPass"];
    FileInfo fileInf = new FileInfo(sourceFile);
    FtpWebRequest reqFTP;

    // Create FtpWebRequest object from the Uri provided
    reqFTP = (FtpWebRequest)(FtpWebRequest.Create(new Uri("ftp://" + ftpServerIP + "//" + targetFile)));

    // Provide the WebPermission Credintials
    reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);

    // Bypass default lan settings
    reqFTP.Proxy = null;

    // By default KeepAlive is true, where the control connection is not closed
    // after a command is executed.
    reqFTP.KeepAlive = false;

    // Specify the command to be executed.
    reqFTP.Method = WebRequestMethods.Ftp.UploadFile;

    // Specify the data transfer type.
    reqFTP.UseBinary = true;

    // Notify the server about the size of the uploaded file
    reqFTP.ContentLength = fileInf.Length;

    // The buffer size is set to 2kb
    int buffLength = 2048;
    Byte[] buff;
    buff = new byte[buffLength];
    int contentLen;

    // Opens a file stream (System.IO.FileStream) to read the file to be uploaded
    FileStream fs = fileInf.OpenRead();

    try
    {
        // Stream to which the file to be upload is written
        Stream strm = reqFTP.GetRequestStream();

        // Read from the file stream 2kb at a time
        long filesize = fs.Length;
        int i=0;
        contentLen = fs.Read(buff, 0, buffLength);

        // Till Stream content ends
        while (contentLen != 0)
        {
            Application.DoEvents();
            // Write Content from the file stream to the FTP Upload Stream
            strm.Write(buff, 0, contentLen);
            contentLen = fs.Read(buff, 0, buffLength);
            i = i + 1;
            //Double percentComp = (i * buffLength) * 100 / filesize;
            //ProgressBar1.Value = (int)percentComp;
        }

        // Close the file stream and the Request Stream
        strm.Close();
        fs.Close();
    }

    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "Upload Error");
    }
}

但是我有一个相反的问题——图片很好,但文件名损坏了。

我知道这是由于编码问题,但我不知道如何使字节数组具有所需的编码......


我不明白,为什么你在第二段代码中会遇到损坏的文件名问题,而在第一段代码中却没有。并且为什么被接受的答案会对此有所帮助。在所有情况下,你都是以完全相同的方式指定文件名。 - Martin Prikryl
很抱歉,马丁。我十年前写了这个问题。我不记得当时的问题和解决方案了。我认为你应该开一个新主题,提出自己的问题。 - Asaf
我没有问题,只是想知道你的问题和答案的意义是什么 :) - Martin Prikryl
我的过去在缠绕着我;我真的不记得了。很可能是我做错了什么(有 bug),甚至有可能是微软的一个已经修复的 bug... - Asaf
6个回答

22

尝试这段代码:

private static void up(string sourceFile, string targetFile)
{            
    try
    {
        string ftpServerIP = ConfigurationManager.AppSettings["ftpIP"];
        string ftpUserID = ConfigurationManager.AppSettings["ftpUser"];
        string ftpPassword = ConfigurationManager.AppSettings["ftpPass"];
        ////string ftpURI = "";
        string filename = "ftp://" + ftpServerIP + "//" + targetFile; 
        FtpWebRequest ftpReq = (FtpWebRequest)WebRequest.Create(filename);
        ftpReq.UseBinary = true;
        ftpReq.Method = WebRequestMethods.Ftp.UploadFile;
        ftpReq.Credentials = new NetworkCredential(ftpUserID, ftpPassword);

        byte[] b = File.ReadAllBytes(sourceFile);

        ftpReq.ContentLength = b.Length;
        using (Stream s = ftpReq.GetRequestStream())
        {
            s.Write(b, 0, b.Length);
        }

        FtpWebResponse ftpResp = (FtpWebResponse)ftpReq.GetResponse();

        if (ftpResp != null)
        {
            MessageBox.Show(ftpResp.StatusDescription);
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.ToString());
    }
}

1
为什么s.Write(b, 0, b.Length);using块内? - Vivian River
4
因为 Stream s 是一个可释放的资源(IDisposable)。 - Jesse C. Slicer
1
在这里,神奇的区别也在于 byte[] b = File.ReadAllBytes(sourceFile); 与其他例子的比较,例如 Encoding.UTF8.GetBytes(stream.ReadToEnd()) - Bimal Poudel

2

读取二进制文件时应该使用Stream,而不是StreamReaderStreamReader 只能读取文本文件。


我已经尝试玩弄这段代码3个小时了,我还发现了带有编码的streamReader(在构造函数中)- 但我无法理解如何使用streamReader或stream对象。对我来说,流就像一个黑匣子:( - Asaf

2
在您的第一个代码示例中,启用二进制传输:FtpWebRequest.UseBinary = true。否则,它将转换各种平台约定之间的文本行结尾(但实际上是图像的一部分)。

一定还有其他东西缺失……我添加了你给我的那行代码,但是没有任何变化。 - Asaf
是的,请参考马克的回答:您正在尝试将文件作为UTF8文本读取。放弃使用StreamReader,直接从流中读取字节,例如使用**Stream.Read()**。 - Paul Ruane

2
你的第二个片段做得很好。它使用了FileStream,而不是StreamReader。StreamReader只适用于文本文件。

0

0

System.Text.Encoding.UTF8.GetBytes(stream.ReadToEnd());

除非您的流内容是文本,否则不要这样做。将您的函数更改为接受一个布尔参数“binary”,并在设置了该标志时使用后一种有效方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接