ฉันจะทำมัลติเธรดในการส่งไฟล์ขนาดใหญ่จาก FTP ไปยังไฟล์ขนาดใหญ่ Azure ได้เร็วขึ้นได้อย่างไร

Aug 19 2020

ขณะนี้ฉันมีรหัสที่ดาวน์โหลดไฟล์จาก FTP ไปยังฮาร์ดดิสก์ในเครื่อง จากนั้นอัปโหลดไฟล์เป็นชิ้น ๆ ไปยัง Azure สุดท้ายมันจะลบไฟล์จาก local และ ftp แม้ว่ารหัสนี้จะช้ามาก แค่อยากรู้ว่าต้องปรับปรุงอย่างไร

    private async Task UploadToBlobJobAsync(FtpConfiguration ftpConfiguration, BlobStorageConfiguration blobStorageConfiguration, string fileExtension)
    {
        try
        {
               ftpConfiguration.FileExtension = fileExtension;

                var filesToProcess = FileHelper.GetAllFileNames(ftpConfiguration).ToList();
                
                var batchSize = 4;
                List<Task> uploadBlobToStorageTasks = new List<Task>(batchSize);

                for (int i = 0; i < filesToProcess.Count(); i += batchSize)
                {
                    // calculated the remaining items to avoid an OutOfRangeException
                    batchSize = filesToProcess.Count() - i > batchSize ? batchSize : filesToProcess.Count() - i;

                    for (int j = i; j < i + batchSize; j++)
                    {
                        var fileName = filesToProcess[j];
                        var localFilePath = SaveFileToLocalAndGetLocation(ftpConfiguration, ftpConfiguration.FolderPath, fileName);

                        // Spin off a background task to process the file we just downloaded
                        uploadBlobToStorageTasks.Add(Task.Run(() =>
                        {
                            // Process the file
                            UploadFile(ftpConfiguration, blobStorageConfiguration, fileName, localFilePath).ConfigureAwait(false);
                        }));
                    }

                    Task.WaitAll(uploadBlobToStorageTasks.ToArray());
                    uploadBlobToStorageTasks.Clear();
                }
        }
        catch (Exception ex)
        {
        }
    }

    private async Task UploadFile(FtpConfiguration ftpConfiguration, BlobStorageConfiguration blobStorageConfiguration, string fileName, string localFilePath)
    {
        try
        {
            await UploadLargeFiles(GetBlobStorageConfiguration(blobStorageConfiguration), fileName, localFilePath).ConfigureAwait(false);
    FileHelper.DeleteFile(ftpConfiguration, fileName); // delete file from ftp
        }
        catch (Exception exception)
        {
        }
    }

   private async Task UploadLargeFiles(BlobStorageConfiguration blobStorageConfiguration, string fileName, string localFilePath)
    {
        try
        {
            var output = await UploadFileAsBlockBlob(localFilePath, blobStorageConfiguration).ConfigureAwait(false);

            // delete the file from local
            Logger.LogInformation($"Deleting {fileName} from the local folder. Path is {localFilePath}.");

            if (File.Exists(localFilePath))
            {
                File.Delete(localFilePath);
            }
        }
        catch (Exception ex)
        {
        }
    }

    private async Task UploadFileAsBlockBlob(string sourceFilePath, BlobStorageConfiguration blobStorageConfiguration)
    {
        string fileName = Path.GetFileName(sourceFilePath);
        try
        {
            var storageAccount = CloudStorageAccount.Parse(blobStorageConfiguration.ConnectionString);
            var blobClient = storageAccount.CreateCloudBlobClient();
            var cloudContainer = blobClient.GetContainerReference(blobStorageConfiguration.Container);
            await cloudContainer.CreateIfNotExistsAsync().ConfigureAwait(false);

            var directory = cloudContainer.GetDirectoryReference(blobStorageConfiguration.Path);
            var blob = directory.GetBlockBlobReference(fileName);

            var blocklist = new HashSet<string>();

            byte[] bytes = File.ReadAllBytes(sourceFilePath);

            const long pageSizeInBytes = 10485760 * 20; // 20mb at a time
            long prevLastByte = 0;
            long bytesRemain = bytes.Length;

            do
            {
                long bytesToCopy = Math.Min(bytesRemain, pageSizeInBytes);
                byte[] bytesToSend = new byte[bytesToCopy];

                Array.Copy(bytes, prevLastByte, bytesToSend, 0, bytesToCopy);

                prevLastByte += bytesToCopy;
                bytesRemain -= bytesToCopy;

                // create blockId
                string blockId = Guid.NewGuid().ToString();
                string base64BlockId = Convert.ToBase64String(Encoding.UTF8.GetBytes(blockId));

                await blob.PutBlockAsync(base64BlockId, new MemoryStream(bytesToSend, true), null).ConfigureAwait(false);

                blocklist.Add(base64BlockId);
            }
            while (bytesRemain > 0);

            // post blocklist
            await blob.PutBlockListAsync(blocklist).ConfigureAwait(false);
        }
        catch (Exception ex)
        {
        }
    }

คำตอบ

1 Blindy Aug 20 2020 at 00:25

ขั้นแรกอย่าเขียนลงดิสก์ในสิ่งที่คุณไม่ต้องการ ยังไม่ชัดเจนว่าเป้าหมายของคุณคืออะไร แต่ฉันไม่เข้าใจว่าทำไมคุณถึงต้องการตั้งแต่แรก

ที่กล่าวว่าหากคุณใช้ฟังก์ชันส่งของคุณในสุญญากาศสิ่งที่ทำในตอนนี้คือ:

  1. อ่านไฟล์ขนาดใหญ่ทั้งหมด (ตามที่คุณพูด) ในหน่วยความจำ

  2. สำหรับแต่ละกลุ่มคุณจะต้องจัดสรรอาร์เรย์ใหม่ทั้งหมดคัดลอกกลุ่มไปวางไว้MemoryStreamด้านบนแล้วส่งไป

นั่นไม่ใช่วิธีการสตรีมมิ่ง

แต่คุณควรเปิดสตรีมไฟล์โดยไม่ต้องอ่านอะไรเลยจากนั้นวนซ้ำเป็นส่วน ๆ ให้มากที่สุดเท่าที่คุณต้องการและอ่านทีละกลุ่มในบัฟเฟอร์ที่จัดสรรไว้ล่วงหน้าหนึ่งชุด (อย่าจัดสรรอาร์เรย์ไบต์ใหม่ต่อไป) รับการแสดง base64 หาก คุณต้องการมันจริงๆและส่งชิ้นส่วนจากนั้นวนซ้ำไปเรื่อย ๆ คนเก็บขยะของคุณจะขอบคุณ