Hỏi cách download chunk/segment sử dụng C# với custom referer header

Discussion in 'Hỏi Đáp Kỹ Thuật' started by dev9x, Feb 12, 2021.

  1. dev9x

    dev9x Sơ Nhập Giang Hồ

    Chúc mừng năm mới toàn thể mn trong diễn đàn nhé. Chúc mn năm mới kiếm đc nhiều xiền. {big_smile}{big_smile}

    mình đang cần code download segment/chunks C#. Server limit download 200kb/s nên mìh cần chia ra nhiều segment để tăng speed lên.
    Mình đang dùng C# nếu dùng WebClient thì set đc custom Referer nhưng lại ko có hàm AddRange để set custom header bytes.
    HtttpRequest thì set đc custom bytes header nhưng lại ko set đc custom Referer, nếu mình set nó báo lỗi
    https://docs.microsoft.com/en-us/dotnet/api/system.net.webheadercollection.isrestricted?view=net-5.0
    Mong đc chỉ giáo cảm ơn các bác.
     
  2. firefox

    firefox Bang Chúng

    bác dùng HttpWebRequest là đủ đồ chơi. Header của HttpWebRequest không phải cái nào cũng set được, có vài header đặc biệt thì set trực tiếp chứ không thông qua headercollection object. Ref em chưa nghịch thử nhưng chắc xài cái này
    Code:
     https://docs.microsoft.com/en-us/dotnet/api/system.net.httpwebrequest.referer?view=net-5.0 
    Ngày trước lượm code trên đây
    Code:
    https://dejanstojanovic.net/aspnet/2018/march/download-file-in-chunks-in-parallel-in-c/
    Giờ customize cũng khá rồi. Hiện tại em đang code như này. Chủ yếu request đầu tiên get sao cho tinh tế để vừa nhanh, nhẹ lại có thể xác định được tổng size sau đó tính toán range đẩy vào các chunks.

    upload_2021-2-12_18-27-0.png

    Chúc bác thành công, đầu xuân khai code sớm quá {bye}
     
    Last edited: Feb 12, 2021
    dev9x likes this.
  3. Dang

    Dang Bang Chúng

    Bạn set như bên dưới là được :
    Code:
    request.Referer = "https://domain.name/path/to/";
     
    dev9x likes this.
  4. dev9x

    dev9x Sơ Nhập Giang Hồ

    Thank 2 bác nhiều nhá. chúc 2 bác năm mới nhiều xièn kk
    --- Double Post Merged, Feb 13, 2021, Original Post Date: Feb 13, 2021 ---
    em cũng dùng code này mà download file mp4 về ko chạy đc bác ạ, ko biết tại sao
    --- Double Post Merged, Feb 13, 2021 ---
    upload_2021-2-13_10-52-53.png
    --- Double Post Merged, Feb 13, 2021 ---
    ko có thông tin về bit rate , độ dài... luôn bác ạ
     
    firefox likes this.
  5. firefox

    firefox Bang Chúng

    chắc lỗi header rồi, bác lấy fiddler debug thử
     
  6. dev9x

    dev9x Sơ Nhập Giang Hồ

    có khi nào do đoạn merge ko bác nhỉ, nếu em truyền thread download > 1 thì file sẽ bị lỗi còn == 1 thì video play bt
    --- Double Post Merged, Feb 13, 2021, Original Post Date: Feb 13, 2021 ---
    upload_2021-2-13_12-51-11.png
    --- Double Post Merged, Feb 13, 2021 ---
    upload_2021-2-13_13-10-40.png
    upload_2021-2-13_13-11-2.png
    upload_2021-2-13_13-11-16.png
    upload_2021-2-13_13-11-41.png
     
  7. firefox

    firefox Bang Chúng

    đoạn đấy ổn mà bác, bác thử truyền =2 xem.
    Với lại cái duration và các field trên hình hồi nãy nó là header, thuộc về chunk đầu tiên. Rất có thể đoạn code nó sắp xếp sai
     
  8. dev9x

    dev9x Sơ Nhập Giang Hồ

    bằng 2 cũng ko đc bác ạ
    --- Double Post Merged, Feb 13, 2021, Original Post Date: Feb 13, 2021 ---
    code của bác có download đc file mp4 ko bác. mình check với file zip cũng ko đc
     
  9. Hoa Mãn Lâu

    Hoa Mãn Lâu Trưởng Môn

    Up full code lên cho ae dễ tư vấn pác
     
  10. dev9x

    dev9x Sơ Nhập Giang Hồ

    PHP:
    using System;
    using System.Collections.Generic;
    using System.Collections.Concurrent;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Threading.Tasks;
    namespace 
    YouTubeRenderTools
    {
        public class 
    DownloadResult
        
    {
            public 
    long Size getset; }
            public 
    String FilePath getset; }
            public 
    TimeSpan TimeTaken getset; }
            public 
    int ParallelDownloads getset; }
            public 
    float Progress;
        }
        public class 
    DownloadParams
        
    {
            public 
    int id;
            public 
    string savePath;
            public 
    string fileName;
            public 
    int totalThreads;
            public 
    Action<floatonProgress;
            public 
    Action onFinish;
        }
        
    internal class Range
        
    {
            public 
    long Start getset; }
            public 
    long End getset; }
        }
        public static class 
    Downloader 
        
    {
            public static 
    object MYlOCK = new object();
            static 
    Downloader() 
            { 
                
    ServicePointManager.Expect100Continue false
                
    ServicePointManager.DefaultConnectionLimit 100
                
    ServicePointManager.MaxServicePointIdleTime 1000
     
            } 
     
            public static 
    DownloadResult Download(string fileUrlstring savePathstring fileNameint totalThreads 0bool validateSSL false)
            {
                if (!
    validateSSL)
                {
                    
    ServicePointManager.ServerCertificateValidationCallback delegate { return true; };
                }

                
    Uri uri = new Uri(fileUrl);

                
    String destinationFilePath Path.Combine(savePathfileName);

                
    DownloadResult result = new DownloadResult() { FilePath destinationFilePath };

                if (
    totalThreads <= 0)
                {
                    
    totalThreads Environment.ProcessorCount;
                }

                
    #region Get file size 
                
    HttpWebRequest webRequest = (HttpWebRequest)HttpWebRequest.Create(fileUrl);
             
    //   webRequest.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36";
             //   webRequest.Headers.Add("Origin", "https://ok.ru");
                
    webRequest.Method "HEAD";
                
    long responseLength;
                
    using (WebResponse webResponse webRequest.GetResponse())
                {
                    
    responseLength long.Parse(webResponse.Headers.Get("Content-Length"));
                    
    result.Size responseLength;
                }
                
    #endregion

                
    Console.WriteLine("responseLength : " responseLength);
                
    Console.WriteLine("destinationFilePath : " destinationFilePath);



                if (
    File.Exists(destinationFilePath))
                {
                    
    File.Delete(destinationFilePath);
                }

                
    using (FileStream destinationStream = new FileStream(destinationFilePathFileMode.Append))
                {
                    
    ConcurrentDictionary<intStringtempFilesDictionary = new ConcurrentDictionary<intString>();

                    
    #region Calculate ranges 
                    
    List<RangereadRanges = new List<Range>();
                    for (
    int chunk 0chunk totalThreads 1chunk++)
                    {
                        var 
    range = new Range()
                        {
                            
    Start chunk * (responseLength totalThreads),
                            
    End = ((chunk 1) * (responseLength totalThreads)) - 1
                        
    };
                        
    readRanges.Add(range);
                    }


                    
    readRanges.Add(new Range()
                    {
                        
    Start readRanges.Any() ? readRanges.Last().End 0,
                        
    End responseLength 1
                    
    });

                    
    #endregion

                    
    DateTime startTime DateTime.Now;

                    
    #region Parallel download 

                    
    int index 0;
                    
    Parallel.ForEach(readRanges, new ParallelOptions() { MaxDegreeOfParallelism totalThreads }, readRange =>
                    {
                        
    HttpWebRequest httpWebRequest HttpWebRequest.Create(fileUrl) as HttpWebRequest;
                      
    //  httpWebRequest.Referer = "https://ok.ru";
                      //  httpWebRequest.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36";
                     //   httpWebRequest.Headers.Add("Origin", "https://ok.ru");
                        
    httpWebRequest.Method "GET";
                        
    httpWebRequest.AddRange(readRange.StartreadRange.End);

                        
    using (HttpWebResponse httpWebResponse httpWebRequest.GetResponse() as HttpWebResponse)
                        {
                            
    String tempFilePath Path.GetTempFileName();
                            
    using (var fileStream = new FileStream(tempFilePathFileMode.CreateFileAccess.WriteFileShare.Write))
                            {
                                
    httpWebResponse.GetResponseStream().CopyTo(fileStream);

                                
    lock (Downloader.MYlOCK)
                                {
                                    
    result.Progress += fileStream.Length;
                                    var 
    percent 1.0 result.Progress result.Size;
                                    
    Console.WriteLine("Progress: " percent);
                                }

                                
    tempFilesDictionary.TryAdd((int)indextempFilePath);
                            }
                        }

                        
    index++;
                    });

                    
    result.ParallelDownloads index;

                    
    #endregion

                    
    result.TimeTaken DateTime.Now.Subtract(startTime);

                    
    #region Merge to single file 
                    
    foreach (var tempFile in tempFilesDictionary.OrderBy(=> b.Key))
                    {
                        
    lock (destinationStream)
                        {
                            
    Console.WriteLine($"tempFile : {tempFile.Key}");
                            
    byte[] tempFileBytes File.ReadAllBytes(tempFile.Value);
                            
    destinationStream.Write(tempFileBytes0tempFileBytes.Length);
                            
    File.Delete(tempFile.Value);
                        }
                      
                    }
                    
    #endregion
                    
    return result;
                }
            }
        }
    }
    --- Double Post Merged, Feb 13, 2021, Original Post Date: Feb 13, 2021 ---
    em up rồi đó bác, bác giúp em với
     
  11. firefox

    firefox Bang Chúng

    bác cho em đại 1 link nào mà bác không download được, em xem thử
     
  12. dev9x

    dev9x Sơ Nhập Giang Hồ

  13. firefox

    firefox Bang Chúng

    Đã sửa và chạy được, code cũ hơi sai cái khúc đã paralell lại còn dùng outer common variable, làm chunk nào về trước sẽ bị xếp trước. Em có ngứa nghề sửa vài chỗ cho chuẩn syntax.
    Năm mới vui vẻ, nhiều view nhé bác!

    PHP:
    using System;
    using System.Collections.Generic;
    using System.Collections.Concurrent;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Threading.Tasks;
    using YouTubeRenderTools.YouTubeRenderTools;


    namespace 
    YouTubeRenderTools
    {
        class 
    Program
        
    {
            static 
    void Main(string[] args)
            {
                
    Directory.CreateDirectory("test");
                var 
    downloadResult Downloader.Download("http://learningcontainer.com/wp-content/uploads/2020/05/sample-mp4-file.mp4""test""test.mp4"totalThreads4);
                
    Console.WriteLine(downloadResult.TimeTaken.TotalMilliseconds.ToString("N0"));
            }
        }


        namespace 
    YouTubeRenderTools
        
    {
            public class 
    DownloadResult
            
    {
                public 
    long Size getset; }
                public 
    String FilePath getset; }
                public 
    TimeSpan TimeTaken getset; }
                public 
    int ParallelDownloads getset; }
                public 
    float Progress;
            }

            public class 
    DownloadParams
            
    {
                public 
    int id;
                public 
    string savePath;
                public 
    string fileName;
                public 
    int totalThreads;
                public 
    Action<floatonProgress;
                public 
    Action onFinish;
            }

            
    internal class Range
            
    {
                public 
    long Start getset; }
                public 
    long End getset; }
                public 
    int ChunkIndex getset; }
            }

            public static class 
    Downloader
            
    {
                static 
    Downloader()
                {
                    
    ServicePointManager.Expect100Continue false;
                    
    ServicePointManager.DefaultConnectionLimit 100;
                    
    ServicePointManager.MaxServicePointIdleTime 1000;
                }

                public static 
    DownloadResult Download(string fileUrlstring savePathstring fileNameint totalThreads 1bool validateSSL false)
                {
                    if (!
    validateSSL)
                    {
                        
    ServicePointManager.ServerCertificateValidationCallback delegate { return true; };
                    }

                    var 
    uri = new Uri(fileUrl);

                    var 
    destinationFilePath Path.Combine(savePathfileName);

                    var 
    result = new DownloadResult {FilePath destinationFilePath};

                    if (
    totalThreads <= 0)
                    {
                        
    totalThreads Environment.ProcessorCount;
                    }

                    
    #region Get file size

                    
    var webRequest = (HttpWebRequestWebRequest.Create(fileUrl);
                    
    //   webRequest.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36";
                    //   webRequest.Headers.Add("Origin", "https://ok.ru");
                    
    webRequest.Method "HEAD";
                    
    long responseLength;
                    
    using (var webResponse webRequest.GetResponse())
                    {
                        
    responseLength long.Parse(webResponse.Headers.Get("Content-Length") ?? "0");
                        
    result.Size responseLength;
                    }

                    
    #endregion

                    
    Console.WriteLine("responseLength : " responseLength);
                    
    Console.WriteLine("destinationFilePath : " destinationFilePath);


                    if (
    File.Exists(destinationFilePath))
                    {
                        
    File.Delete(destinationFilePath);
                    }

                    
    using (var destinationStream = new FileStream(destinationFilePathFileMode.Append))
                    {
                        var 
    tempFilesDictionary = new ConcurrentDictionary<intString>();

                        
    #region Calculate ranges

                        
    var readRanges = new List<Range>();
                        for (var 
    chunk 0chunk totalThreads 1chunk++)
                        {
                            var 
    range = new Range
                            
    {
                                
    Start chunk * (responseLength totalThreads),
                                
    End = (chunk 1) * (responseLength totalThreads) - 1,
                                
    ChunkIndex chunk
                            
    };
                            
    readRanges.Add(range);
                        }


                        
    readRanges.Add(new Range
                        
    {
                            
    Start readRanges.Any() ? readRanges.Last().End 0,
                            
    End responseLength 1,
                            
    ChunkIndex readRanges.Count 1
                        
    });

                        
    #endregion

                        
    var startTime DateTime.Now;

                        
    #region Parallel download

                        
    Parallel.ForEach(readRanges, new ParallelOptions {MaxDegreeOfParallelism totalThreads}, readRange =>
                        {
                            var 
    httpWebRequest WebRequest.Create(fileUrl) as HttpWebRequest;
                            
    //  httpWebRequest.Referer = "https://ok.ru";
                            //  httpWebRequest.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36";
                            //   httpWebRequest.Headers.Add("Origin", "https://ok.ru");
                            
    httpWebRequest.Method "GET";
                            
    httpWebRequest.AddRange(readRange.StartreadRange.End);

                            
    using var httpWebResponse httpWebRequest.GetResponse() as HttpWebResponse;
                            var 
    tempFilePath Path.GetTempFileName();
                            
    using var fileStream = new FileStream(tempFilePathFileMode.CreateFileAccess.WriteFileShare.Write);
                            
    httpWebResponse.GetResponseStream().CopyTo(fileStream);

                            
    lock ("Downloader.Events")
                            {
                                
    result.Progress += fileStream.Length;
                                var 
    percent 1.0 result.Progress result.Size;
                                
    Console.WriteLine("Progress: " percent);
                            }

                            
    tempFilesDictionary.TryAdd(readRange.ChunkIndextempFilePath);
                        });

                        
    result.ParallelDownloads totalThreads;

                        
    #endregion

                        
    result.TimeTaken DateTime.Now.Subtract(startTime);

                        
    #region Merge to single file

                        
    foreach (var tempFile in tempFilesDictionary.OrderBy(=> b.Key))
                        {
                            
    lock (destinationStream)
                            {
                                
    Console.WriteLine($"tempFile : {tempFile.Key}");
                                var 
    tempFileBytes File.ReadAllBytes(tempFile.Value);
                                
    destinationStream.Write(tempFileBytes0tempFileBytes.Length);
                                
    File.Delete(tempFile.Value);
                            }
                        }

                        
    #endregion

                        
    return result;
                    }
                }
            }
        }
    }
     
    Hoa Mãn Lâu and dev9x like this.
  14. dev9x

    dev9x Sơ Nhập Giang Hồ

    Thank bác, chạy đc rồi bác. bác đỉnh quá, em mò cả ngày ko đc. với cả cho em hỏi ngu với outer common variable là sao hả bác. Thank bác lần nữa :D
     
  15. firefox

    firefox Bang Chúng

    à 1 cách gọi của tụi dev .net trên stackoverflow thôi, em gõ ngược thứ tự nên search không ra đấy :)), nôm na thì mỗi luồng trong cái khối Parallel.ForEach nó là 1 scope chạy async với nhau. Các scope đấy ở code cũ dùng chung 1 biến khai báo bên ngoài thì sẽ xảy ra hiện tượng này, cách fix đơn giản là đặt thêm biến phụ, hoặc mạnh OOP thì nhét nó vào class Range luôn
     
    Last edited: Feb 13, 2021
    dev9x likes this.
  16. dev9x

    dev9x Sơ Nhập Giang Hồ

    Thank bác, em code gà quá cần phải học hỏi nhiều. May quá vẫn xin đc việc =))
     
  17. firefox

    firefox Bang Chúng

    .net nếu ở sg mà skill ổn ổn thiếu việc thì có thể hú mình. 9x học bách khoa cơ khí xong chuyển qua làm IT đây :v :v
     
    dev9x likes this.
  18. dev9x

    dev9x Sơ Nhập Giang Hồ

    ái chà bác học bách khoa bảo sao, em học cđ thôi ạ. em đang làm dev unity2d
    --- Double Post Merged, Feb 13, 2021, Original Post Date: Feb 13, 2021 ---
    System.IO.IOException: 'The process cannot access the file 'C:\Users\Admin\AppData\Local\Temp\tmpC981.tmp' because it is being used by another process.'
    --- Double Post Merged, Feb 13, 2021 ---
    giờ lần nào cũng bị lỗi này b ơi, em restart máy rồi vẫn bị
     
  19. firefox

    firefox Bang Chúng

    nó bắn ở dòng nào thế bác
     
  20. dev9x

    dev9x Sơ Nhập Giang Hồ

    đây bác ạ
    upload_2021-2-13_21-58-37.png


    using var httpWebResponse = httpWebRequest.GetResponse() as HttpWebResponse;
    var tempFilePath = Path.GetTempFileName();
    using var fileStream = new FileStream(tempFilePath, FileMode.Create, FileAccess.Write, FileShare.Write);

    em sửa bỏ đoạn using kia đc có liên quan ko bác, tại của em nó chưa hỗ trợ kiểu đó

    upload_2021-2-13_21-59-2.png
    --- Double Post Merged, Feb 13, 2021, Original Post Date: Feb 13, 2021 ---
    em fix đc rồi thank bác.kk
    fileStream.Close();
    httpWebResponse.Close();
     
    firefox likes this.