【问题标题】:Transfer files directly from FTP to Azure File Storage without keeping them locally in memory or disk将文件直接从 FTP 传输到 Azure 文件存储,而无需将它们本地保存在内存或磁盘中
【发布时间】:2017-12-01 18:14:34
【问题描述】:

我必须将文件从 FTP 传输到 Azure 文件存储。我的代码工作正常,但我将这些文件传输到内存中,这不是最佳实践。所以首先我将流读入内存中的Byte 数组。然后我将输出上传到 Azure 文件存储。

现在我知道最好异步执行此操作。但我不知道这是否可能以及如何做到这一点。

我的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.WindowsAzure.Storage;
using System.Configuration;
using Microsoft.WindowsAzure.Storage.File;
using System.IO;
using Microsoft.Azure;
using System.Net;

namespace TransferFtpToAzure
{
    class Program
    {
        public static void Main(string[] args)
        {
            List<FileName> sourceFileList = new List<FileName>();
            List<FileName> targetFileList = new List<FileName>();

            string targetShareReference = ConfigurationManager.AppSettings["AzureShare"];
            string targetDirectoryReference = ConfigurationManager.AppSettings["Environment"] + "/" + Enums.AzureFolders.Mos + "/" + Enums.AzureFolders.In;
            string sourceURI = (ConfigurationManager.AppSettings["FtpConnectionString"] + ConfigurationManager.AppSettings["Environment"].ToUpper() +"/"+ Enums.FtpFolders.Mos + "/").Replace("\\","/");
            string sourceUser = ConfigurationManager.AppSettings["FtpServerUserName"];
            string sourcePass = ConfigurationManager.AppSettings["FtpServerPassword"];

            getFileLists(sourceURI, sourceUser, sourcePass, sourceFileList, targetShareReference, targetDirectoryReference, targetFileList);

            Console.WriteLine(sourceFileList.Count + " files found!");

            CheckLists(sourceFileList, targetFileList);
            targetFileList.Sort();

            Console.WriteLine(sourceFileList.Count + " unique files on sourceURI" + Environment.NewLine + "Attempting to move them.");

            foreach (var file in sourceFileList)
            {
                try
                {
                    CopyFile(file.fName, sourceURI, sourceUser, sourcePass, targetShareReference, targetDirectoryReference);
                }
                catch
                {
                    Console.WriteLine("There was move error with : " + file.fName);
                }
            }
        }

        public class FileName : IComparable<FileName>
        {
            public string fName { get; set; }
            public int CompareTo(FileName other)
            {
                return fName.CompareTo(other.fName);
            }
        }

        public static void CheckLists(List<FileName> sourceFileList, List<FileName> targetFileList)
        {
            for (int i = 0; i < sourceFileList.Count; i++)
            {
                if (targetFileList.BinarySearch(sourceFileList[i]) > 0)
                {
                    sourceFileList.RemoveAt(i);
                    i--;
                }
            }
        }

        public static void getFileLists(string sourceURI, string sourceUser, string sourcePass, List<FileName> sourceFileList, string targetShareReference, string targetDirectoryReference, List<FileName> targetFileList)
        {
            string line = "";
            /////////Source FileList
            FtpWebRequest sourceRequest;
            sourceRequest = (FtpWebRequest)WebRequest.Create(sourceURI);
            sourceRequest.Credentials = new NetworkCredential(sourceUser, sourcePass);
            sourceRequest.Method = WebRequestMethods.Ftp.ListDirectory;
            sourceRequest.UseBinary = true;
            sourceRequest.KeepAlive = false;
            sourceRequest.Timeout = -1;
            sourceRequest.UsePassive = true;
            FtpWebResponse sourceRespone = (FtpWebResponse)sourceRequest.GetResponse();
            //Creates a list(fileList) of the file names
            using (Stream responseStream = sourceRespone.GetResponseStream())
            {
                using (StreamReader reader = new StreamReader(responseStream))
                {
                    line = reader.ReadLine();
                    while (line != null)
                    {
                        var fileName = new FileName
                        {
                            fName = line
                        };
                        sourceFileList.Add(fileName);
                        line = reader.ReadLine();
                    }
                }
            }
            /////////////Target FileList
            CloudStorageAccount storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("StorageConnectionString"));
            CloudFileClient fileClient = storageAccount.CreateCloudFileClient();
            //var test = fileClient.ListShares();
            CloudFileShare fileShare = fileClient.GetShareReference(targetShareReference);
            if (fileShare.Exists())
            {
                CloudFileDirectory rootDirectory = fileShare.GetRootDirectoryReference();
                if (rootDirectory.Exists())
                {
                    CloudFileDirectory customDirectory = rootDirectory.GetDirectoryReference(targetDirectoryReference);
                    if (customDirectory.Exists())
                    {
                        var fileCollection = customDirectory.ListFilesAndDirectories().OfType<CloudFile>();
                        foreach (var item in fileCollection)
                        {
                            var fileName = new FileName
                            {
                                fName = item.Name
                            };
                            targetFileList.Add(fileName);
                        }
                    }
                }
            }
        }

        public static void CopyFile(string fileName, string sourceURI, string sourceUser, string sourcePass, string targetShareReference, string targetDirectoryReference)
        {
            try
            {
                FtpWebRequest request = (FtpWebRequest)WebRequest.Create(sourceURI + fileName);
                request.Method = WebRequestMethods.Ftp.DownloadFile;
                request.Credentials = new NetworkCredential(sourceUser, sourcePass);
                FtpWebResponse response = (FtpWebResponse)request.GetResponse();
                Stream responseStream = response.GetResponseStream();
                Upload(fileName, ToByteArray(responseStream), targetShareReference, targetDirectoryReference);
                responseStream.Close();
            }
            catch
            {
                Console.WriteLine("There was an error with :" + fileName);
            }
        }

        public static Byte[] ToByteArray(Stream stream)
        {
            MemoryStream ms = new MemoryStream();
            byte[] chunk = new byte[4096];
            int bytesRead;
            while ((bytesRead = stream.Read(chunk, 0, chunk.Length)) > 0)
            {
                ms.Write(chunk, 0, bytesRead);
            }

            return ms.ToArray();
        }

        public static bool Upload(string FileName, byte[] Image, string targetShareReference, string targetDirectoryReference)
        {
            try
            {
                CloudStorageAccount storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("StorageConnectionString"));
                CloudFileClient fileClient = storageAccount.CreateCloudFileClient();
                //var test = fileClient.ListShares();
                CloudFileShare fileShare = fileClient.GetShareReference(targetShareReference);
                if (fileShare.Exists())
                {
                    CloudFileDirectory rootDirectory = fileShare.GetRootDirectoryReference();
                    if (rootDirectory.Exists())
                    {
                        CloudFileDirectory customDirectory = rootDirectory.GetDirectoryReference(targetDirectoryReference);
                        if (customDirectory.Exists())
                        {
                            var cloudFile = customDirectory.GetFileReference(FileName);
                            using (var stream = new MemoryStream(Image, writable: false))
                            {
                                cloudFile.UploadFromStream(stream);
                            }
                        }
                    }
                }
                return true;
            }
            catch
            {
                return false;
            }
        }
    }
}

【问题讨论】:

    标签: c# azure ftp azure-storage ftpwebrequest


    【解决方案1】:

    如果我理解正确,您希望避免在下载和上传之间将文件存储在内存中。

    请参阅:
    Azure function to copy files from FTP to blob storage。

    【讨论】:

    • 确实,这是我想要避免的。以为您需要使用异步方法:)。谢谢!!
    • “由于对象的当前状态,操作无效。”,有什么想法吗?
    • @AaYy 考虑提出一个新问题并确保包含minimal reproducible example。
    • @AaYy,你好,你能取消删除帖子吗?是否可以使用 file.UploadText() 将文件从 FTP 传输到文件存储?
    【解决方案2】:

    使用 Azure 存储文件共享,这是它对我有用的唯一方法,无需将整个 ZIP 加载到内存中。我测试了一个 3GB 的 ZIP 文件(包含数千个文件或内部有一个大文件),内存/CPU 低且稳定。希望对你有帮助!

    var zipFiles = _directory.ListFilesAndDirectories()
        .OfType<CloudFile>()
        .Where(x => x.Name.ToLower().Contains(".zip"))
        .ToList();
    
    foreach (var zipFile in zipFiles)
    {
        using (var zipArchive = new ZipArchive(zipFile.OpenRead()))
        {
            foreach (var entry in zipArchive.Entries)
            {
                if (entry.Length > 0)
                {
                    CloudFile extractedFile = _directory.GetFileReference(entry.Name);
    
                    using (var entryStream = entry.Open())
                    {
                        byte[] buffer = new byte[16 * 1024];
                        using (var ms = extractedFile.OpenWrite(entry.Length))
                        {
                            int read;
                            while ((read = entryStream.Read(buffer, 0, buffer.Length)) > 0)
                            {
                                ms.Write(buffer, 0, read);
                            }
                        }
                    }
                }
            }
        }               
    }
    

    【讨论】:

    • 您可以使用 await extractedFile.UploadFromStreamAsync(entryStream) 之类的东西来代替大部分代码。
    • 其实那是我的第一次尝试。但是这种方法需要内存中已经加载了一个 MemoryStream,否则会报错“Operation is not valid due to the current state of the object”。被解雇了。
    • 另外,当流来自WebRequest(就像我一样)时,不能使用UploadFromStreamAsync,因为响应流将CanSeek设置为false。
    猜你喜欢
    • 2015-07-13
    • 2019-05-21
    • 2022-11-15
    • 1970-01-01
    • 2013-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多