VSVC简单的WinInet的FTP上传的程序,解决中文文件名乱码问题UTF8

2022.1

安卓手机装了primitive ftpd的FTP服务器,方便管理文件。安卓都是UTF8。

WIN10是GB字符集,可以直接用WIN10的资源管理器直接看FTP服务器和下载,但无法上传,提示501 invalid character in command,应是字符集问题。
命令行的ftp,ls时就出现乱码,即服务器是UTF8,本地是GB,字符集不匹配。
用filezilla client软件,一切正常,会自动检测字符集。一般就用它。
用ncFTP,ncftpput上传单个文件,文件名是中文时不正常。放弃使用ncFTP。

FTP传输是支持8bit的,应该是ISO-8859-1扩展成UTF8,所以转成UTF8应就OK。于是自己编一个FTP简单上传程序。
采用WinInet比较简单。
但是,自编程序,转成UTF8后,竟然也有问题,出错,返回值是12003。
这个是WinInet的CFtpConnection::PutFile的问题。坑太多。
根据网友的经验解决。


参考:

http://cn.voidcc.com/question/p-zwzxiksr-bky.html
FTP协议在RFC 959,其结果发表在1985年 FTP协议被设计在原始Telnet协议的顶部上, 指定在RFC 854中指定的远程登录 的相关部分有关FTP的规范是涵盖Network Virtual 终端(NVT)的规范。
根据RFC 854,NVT要求使用 (7位)ASCII作为字符集。使用任何其他字符集 需要明确的协商。该字符集只包含127 不同的字符:英文字母和数字,标点符号 字符和一些控制字符。在ASCII字符集中不包含其他脚本的重音字母,变音符号或 。
为了支持非英文字符 ,1999年在RFC 2640中扩展了FTP规范 。该扩展需要使用UTF-8 作为字符集。该字符集是 ASCII的严格超集,每个有效的ASCII字符也是 UTF-8中的相同字符。 UTF-8字符集可以显示任何有效的Unicode 字符。这包括变音符号,重音字母以及不同的 脚本。此扩展与RFC 959完全向后兼容。由于只使用英文字符,所以您使用的软件是否支持RFC 2640并不重要。但是,如果在不使用RFC 2640兼容软件 的情况下使用非英文字符 ,则会出现问题 - 这些问题完全是由自己制定的,而不是由遵守规范的 来完成。


程序来自《vc实现简单的ftp上传和下载功能》:https://blog.csdn.net/fjssharpsword/article/details/6088500

《FTP采用UTF8编码上传文件名中含有奇数个汉字时出错的解决方法》:https://blog.csdn.net/lizhq007/article/details/8233515

《CFtpConnection PutFile 中文路径utf-8 转码后 上传失败》:https://bbs.csdn.net/topics/391844331

《WinInet 基础知识》:https://docs.microsoft.com/zh-cn/cpp/mfc/wininet-basics?view=msvc-170

程序如下:
MFC对话框,多字节字符工程环境,拖动文件过来后就上传:

#include <afxinet.h>     /


void CDIAGMFCFTPDlg::OnDropFiles(HDROP hDropInfo)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    char lpszFileName[MAX_PATH + 1] = { 0 };
    CString str_file;
    int nFileCount;
    nFileCount = ::DragQueryFile(hDropInfo, 0xFFFFFFFF, NULL, 512);

    if (nFileCount == 1)
    {
        ::DragQueryFile(hDropInfo, 0, lpszFileName, MAX_PATH);
        str_file = lpszFileName;     //文件名
        
    }
    else
    {
        ::AfxMessageBox("请勿拖放多个文件!");
    }
    ::DragFinish(hDropInfo);       //释放内存


    myFTPupload(str_file);   
    

    CDialogEx::OnDropFiles(hDropInfo);
}


void CDIAGMFCFTPDlg::myFTPupload(CString LocalPath)
{
    // TODO: 在此处添加实现代码.
    CString csdisplay="本地源文件: \r\n";       
    csdisplay += LocalPath;
    csdisplay += "\r\n\r\n";

    //CString LocalPath;        //本地的文件名
    CString FtpPath;        //放在服务器的文件名

    FtpPath = PathFindFileName(LocalPath);
    //MessageBox(FtpPath);
    csdisplay += "服务器目标文件: \r\n";
    csdisplay += FtpPath;
    csdisplay += "\r\n\r\n";
    csdisplay += "结果: \r\n";

    //我的FTP服务器是采用UTF8,这里将FTP路径改成UTF8格式。
    //多字节字符先转UNICODE宽字符,再转UTF8

    // 多字节字符 转 unicode宽字符
    char wbuff[4000];
    int wi;
    wi = MultiByteToWideChar(CP_ACP, 0, FtpPath, -1, (LPWSTR)wbuff, 2000);
    wbuff[wi * 2] = 0;
    wbuff[wi * 2 + 1] = 0;
    if (wi == 0)   //转换不成功
    {
        //MessageBox("error 文件名转换成UTF8不成功");
        csdisplay += "error 文件名转换成UTF8不成功\r\n";
        goto WOUT;
    }

    //unicode宽字符 转 UTF8
    char wbuff2[4000];
    wi = WideCharToMultiByte(CP_UTF8, NULL, (LPCWCH)wbuff, -1, wbuff2, 4000, 0, 0);
    wbuff2[wi] = 0;
    if (wi == 0)
    {
        //MessageBox("error 文件名转换成UTF8不成功");
        csdisplay += "error 文件名转换成UTF8不成功\r\n";
        goto WOUT;
    }

    char csserver[2000];
    int myport;

    GetDlgItemText(IDC_EDIT1, csserver,  2000);
    myport = GetDlgItemInt(IDC_EDIT2);

    //程序来自《vc实现简单的ftp上传和下载功能》
    CInternetSession *m_pInternetSession = new CInternetSession(AfxGetAppName(), 1, PRE_CONFIG_INTERNET_ACCESS);
    CFtpConnection *m_pFtpConnection = NULL;
    try
    {
        m_pFtpConnection = m_pInternetSession->GetFtpConnection(csserver, "anonymous", "", myport, 1);
        //   LPCTSTR pstrServer,  LPCTSTR pstrUserName,  LPCTSTR pstrPassword , INTERNET_PORT nPort,  BOOL bPassive
        //  被动是服务器开2个端口。  主动是服务器要连接到client另一个口(有防火墙问题)

        if (m_pFtpConnection != NULL)  //链接成功
        {

            // CFtpFileFind FtpFinder(m_pFtpConnection);   

            /*
            if (!(m_pFtpConnection->GetFile(FtpPath, LocalPath, true, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_BINARY, 1)))
            {
                DWORD dErr = GetLastError();
                CString sErr;
                sErr.Format("下载失败: ERROR %d", dErr);
                AfxMessageBox(sErr);
            }
            */


            //这里仅做上传。FTP上传文件名称中文乱码,二个方法解决

            //  方法一  成功,先上传一个假文件名,再重命名。
            //《FTP采用UTF8编码上传文件名中含有奇数个汉字时出错的解决方法》:先弄个临时的文件名,不要含有中文,待他上传完成后,调用rename把刚才的临时文件名重命名为自己想要的名字。在上传完成后马上进行重命名操作会失败,是间隔时间上的问题,需要在rename之前加上延时或者循环直至成功。

            //上传文件,用特定文件名
            if (!(m_pFtpConnection->PutFile(LocalPath, "~_T1e2M3p4_~.wxlftp", FTP_TRANSFER_TYPE_BINARY, 1)))
            {
                DWORD dErr = GetLastError();
                CString sErr;
                sErr.Format("上传失败: ERROR %d", dErr);
                //AfxMessageBox(sErr);
                csdisplay += sErr;  csdisplay += "\r\n";
            }

            //重命名文件
            int isleep = 0;
            while (!m_pFtpConnection->Rename("~_T1e2M3p4_~.wxlftp", wbuff2))
            {
                Sleep(1);    //1ms
                if (isleep++ > 5000)
                {
                    //MessageBox("error rename file.");
                    csdisplay += "error rename file.";  csdisplay += "\r\n";
                    break;
                }
            }

            csdisplay += "成功!\r\n";  


            /*
            //  方法二  可行,未完善
            //《CFtpConnection PutFile 中文路径utf-8 转码后 上传失败》:采用CFtpConnection 类中基本方法OpenFile()(会自动在ftp上创建文件名不存在的文件), 和CInternetFile 的Write(), Close()实现文件传输,即可避免
            CInternetFile *ftpfile = m_pFtpConnection->OpenFile(wbuff2, GENERIC_WRITE, FTP_TRANSFER_TYPE_BINARY, 1);
            ftpfile->Write(wbuff2, 100);
            ftpfile->Close();
            */
        }
    }
    catch (CInternetException *pEx)
    {
        TCHAR szError[1024];
        if (pEx->GetErrorMessage(szError, 1024))
        {
            //AfxMessageBox(szError);
            csdisplay += "ERROR: "; csdisplay += szError;  csdisplay += "\r\n";
        }
        else
        {
            //AfxMessageBox("There was an exception");
            csdisplay += "ERROR: There was an exception";  csdisplay += "\r\n";
        }
        pEx->Delete();
    }

    if (m_pFtpConnection != NULL)
    {
        m_pFtpConnection->Close();
        delete m_pFtpConnection;
    }
    delete m_pInternetSession;

WOUT:
    CTime t1 = CTime::GetCurrentTime();
    csdisplay += t1.Format("%Y-%m-%d %H:%M:%S -%A");
    csdisplay += "\r\n";
    SetDlgItemText(IDC_EDIT3, csdisplay);

}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值