programing

C#에서 대용량 파일을 바이트 배열로 읽는 가장 좋은 방법은 무엇입니까?

mailnote 2023. 5. 26. 22:08
반응형

C#에서 대용량 파일을 바이트 배열로 읽는 가장 좋은 방법은 무엇입니까?

저는 대용량 이진 파일(수 메가바이트)을 바이트 배열로 읽는 웹 서버를 가지고 있습니다.서버가 여러 개의 파일을 동시에 읽을 수 있기 때문에(다른 페이지 요청) CPU에 부담을 주지 않고 가장 최적화된 방법을 찾고 있습니다.아래 코드가 충분합니까?

public byte[] FileToByteArray(string fileName)
{
    byte[] buff = null;
    FileStream fs = new FileStream(fileName, 
                                   FileMode.Open, 
                                   FileAccess.Read);
    BinaryReader br = new BinaryReader(fs);
    long numBytes = new FileInfo(fileName).Length;
    buff = br.ReadBytes((int) numBytes);
    return buff;
}

전체를 다음으로 간단히 대체:

return File.ReadAllBytes(fileName);

그러나 메모리 사용량이 걱정되는 경우에는 전체 파일을 메모리로 한꺼번에 읽어 들여서는 안 됩니다.그것은 덩어리로 해야 합니다.

저는 여기서 일반적으로 대답은 "하지 마세요"라고 주장할 수 있습니다.한 번에 모든 데이터가 필요한 경우를 제외하고는Stream기반 API(또는 일부 변형된 리더/반복기)입니다.이는 시스템 부하를 최소화하고 처리량을 최대화하기 위해 (질문에서 제안한 바와 같이) 여러 병렬 작업을 수행하는 경우에 특히 중요합니다.

예를 들어, 데이터를 호출자에게 스트리밍하는 경우:

Stream dest = ...
using(Stream source = File.OpenRead(path)) {
    byte[] buffer = new byte[2048];
    int bytesRead;
    while((bytesRead = source.Read(buffer, 0, buffer.Length)) > 0) {
        dest.Write(buffer, 0, bytesRead);
    }
}

저는 이렇게 생각합니다.

byte[] file = System.IO.File.ReadAllBytes(fileName);

당신의 코드는 (파일 대신) 여기에 반영될 수 있습니다.모든 바이트 읽기):

public byte[] ReadAllBytes(string fileName)
{
    byte[] buffer = null;
    using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
    {
        buffer = new byte[fs.Length];
        fs.Read(buffer, 0, (int)fs.Length);
    }
    return buffer;
} 

정수를 기록합니다.MaxValue - Read 메서드에 의해 배치된 파일 크기 제한입니다.즉, 한 번에 2GB 청크만 읽을 수 있습니다.

또한 FileStream의 마지막 인수는 버퍼 크기입니다.

또한 FileStream과 BufferedStream대해 읽는 것을 제안합니다.

항상 그렇듯이 가장 빠른 프로파일링을 위한 간단한 샘플 프로그램이 가장 유용할 것입니다.

또한 기본 하드웨어는 성능에 큰 영향을 미칩니다.대용량 캐시가 있는 서버 기반 하드 디스크 드라이브와 온보드 메모리 캐시가 있는 RAID 카드를 사용하고 있습니까?아니면 IDE 포트에 연결된 표준 드라이브를 사용하고 있습니까?

작업 빈도, 파일 크기 및 보고 있는 파일 수에 따라 고려해야 할 다른 성능 문제가 있습니다.한 가지 기억해야 할 것은 각 바이트 배열이 가비지 수집기의 자비에 의해 해제된다는 것입니다.이러한 데이터를 캐싱하지 않으면 많은 가비지가 발생하고 GC에서 성능이 %Time으로 손실될 수 있습니다.청크가 85K보다 크면 Large Object Heap(LOH)에 할당되므로 모든 세대의 컬렉션을 확보해야 합니다(이는 매우 비용이 많이 들고 서버에서 진행되는 동안 모든 실행이 중지됩니다).또한 LOH에 많은 개체가 있는 경우 LOH 단편화(LOH는 압축되지 않음)가 발생하여 성능이 저하되고 메모리 부족 예외가 발생할 수 있습니다.특정 지점에 도달하면 프로세스를 재활용할 수 있지만, 그게 최선의 방법인지는 모르겠습니다.

요점은 모든 바이트를 메모리에 최대한 빨리 읽기 전에 앱의 전체 수명 주기를 고려해야 한다는 것입니다. 그렇지 않으면 전체 성능을 위해 단기 성능을 거래할 수 있습니다.

그렇겠지BinaryReader버퍼의 길이를 얻기 위한 코드 줄 대신에 다음과 같이 리팩터링할 수 있습니다.

public byte[] FileToByteArray(string fileName)
{
    byte[] fileData = null;

    using (FileStream fs = File.OpenRead(fileName)) 
    { 
        using (BinaryReader binaryReader = new BinaryReader(fs))
        {
            fileData = binaryReader.ReadBytes((int)fs.Length); 
        }
    }
    return fileData;
}

사용하는 것보다 더 낫습니다..ReadAllBytes()다음을 포함한 상위 응답의 댓글에서 보았기 때문입니다..ReadAllBytes()해설자 중 한 명이 600MB 이상의 파일에 문제가 있다는 것, 이후.BinaryReader이런 종류의 것을 위한 것입니다.또한, 그것을안넣것는에▁a것▁it▁also넣는▁in,에안,▁putting을.using는 진은보니다합장술▁the다▁ensures▁statement를 보장합니다.FileStream그리고.BinaryReader폐쇄 및 폐기됩니다.

'large file'이 4GB 제한을 초과하는 것을 의미하는 경우, 저의 다음과 같은 서면 코드 로직이 적절합니다.주의해야 할 주요 문제는 SEEK 방법과 함께 사용되는 LONG 데이터 유형입니다.LONG은 2^32 데이터 경계 너머를 가리킬 수 있습니다.이 예에서 코드는 먼저 큰 파일을 1GB 청크로 처리하고, 큰 전체 1GB 청크를 처리한 후 남은(<1GB) 바이트를 처리합니다.저는 이 코드를 4GB 크기 이상의 파일의 CRC를 계산할 때 사용합니다.(이 예에서 crc32c 계산에 https://crc32c.machinezoo.com/ 사용)

private uint Crc32CAlgorithmBigCrc(string fileName)
{
    uint hash = 0;
    byte[] buffer = null;
    FileInfo fileInfo = new FileInfo(fileName);
    long fileLength = fileInfo.Length;
    int blockSize = 1024000000;
    decimal div = fileLength / blockSize;
    int blocks = (int)Math.Floor(div);
    int restBytes = (int)(fileLength - (blocks * blockSize));
    long offsetFile = 0;
    uint interHash = 0;
    Crc32CAlgorithm Crc32CAlgorithm = new Crc32CAlgorithm();
    bool firstBlock = true;
    using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
    {
        buffer = new byte[blockSize];
        using (BinaryReader br = new BinaryReader(fs))
        {
            while (blocks > 0)
            {
                blocks -= 1;
                fs.Seek(offsetFile, SeekOrigin.Begin);
                buffer = br.ReadBytes(blockSize);
                if (firstBlock)
                {
                    firstBlock = false;
                    interHash = Crc32CAlgorithm.Compute(buffer);
                    hash = interHash;
                }
                else
                {
                    hash = Crc32CAlgorithm.Append(interHash, buffer);
                }
                offsetFile += blockSize;
            }
            if (restBytes > 0)
            {
                Array.Resize(ref buffer, restBytes);
                fs.Seek(offsetFile, SeekOrigin.Begin);
                buffer = br.ReadBytes(restBytes);
                hash = Crc32CAlgorithm.Append(interHash, buffer);
            }
            buffer = null;
        }
    }
    //MessageBox.Show(hash.ToString());
    //MessageBox.Show(hash.ToString("X"));
    return hash;
}

개요: 이미지가 action= 내장 리소스로 추가된 경우 GetExecutingAssembly를 사용하여 jpg 리소스를 스트림으로 검색한 다음 스트림의 이진 데이터를 바이트 배열로 읽습니다.

   public byte[] GetAImage()
    {
        byte[] bytes=null;
        var assembly = Assembly.GetExecutingAssembly();
        var resourceName = "MYWebApi.Images.X_my_image.jpg";

        using (Stream stream = assembly.GetManifestResourceStream(resourceName))
        {
            bytes = new byte[stream.Length];
            stream.Read(bytes, 0, (int)stream.Length);
        }
        return bytes;

    }

C#의 BufferedStream 클래스를 사용하여 성능을 향상시킵니다.버퍼는 데이터를 캐시하는 데 사용되는 메모리의 바이트 블록으로, 운영 체제에 대한 호출 수를 줄입니다.버퍼는 읽기 및 쓰기 성능을 향상시킵니다.

코드 예제 및 추가 설명은 다음을 참조하십시오. http://msdn.microsoft.com/en-us/library/system.io.bufferedstream.aspx

사용:

 bytesRead = responseStream.ReadAsync(buffer, 0, Length).Result;

나는 그것을 시도하는 것을 추천합니다.Response.TransferFile() 그 에 a 그고리법aResponse.Flush()그리고.Response.End()대용량 파일을 제공합니다.

2GB 이상의 파일을 다루는 경우 위의 방법이 실패한다는 것을 알게 될 것입니다.

스트림을 MD5로 넘겨 파일을 청크하는 것이 훨씬 쉽습니다.

private byte[] computeFileHash(string filename)
{
    MD5 md5 = MD5.Create();
    using (FileStream fs = new FileStream(filename, FileMode.Open))
    {
        byte[] hash = md5.ComputeHash(fs);
        return hash;
    }
}

언급URL : https://stackoverflow.com/questions/2030847/best-way-to-read-a-large-file-into-a-byte-array-in-c

반응형