C #에서 파일에 스트림을 어떻게 저장합니까?


713

나는이 StreamReader지금은 디스크에이 스트림을 저장 (스트림이 될 5 월 싶어, 내가 스트림으로 초기화 것을 객체 .gif또는 .jpg.pdf).

기존 코드 :

StreamReader sr = new StreamReader(myOtherObject.InputStream);
  1. 이것을 디스크에 저장해야합니다 (파일 이름이 있습니다).
  2. 앞으로 나는 이것을 SQL Server에 저장하고 싶을 수도 있습니다.

인코딩 유형도 있는데 SQL Server에 저장하는 데 필요한 인코딩 유형입니까?


1
myOtherObject 란 무엇입니까?
anhtv13

2
이 질문에 여전히 대답이 없습니까?
Brett Rigby

@BrettRigby Jon Skeet 답변이 있습니다. 거의 자동으로 승인됩니다. D
Ricardo Dias Morais

답변:


912

Jon Skeet의 답변에서 Tilendor가 강조한 것처럼 스트림에는 CopyTo.NET 4 이후 방법이 있습니다.

var fileStream = File.Create("C:\\Path\\To\\File");
myOtherObject.InputStream.Seek(0, SeekOrigin.Begin);
myOtherObject.InputStream.CopyTo(fileStream);
fileStream.Close();

또는 다음 using구문을 사용하십시오.

using (var fileStream = File.Create("C:\\Path\\To\\File"))
{
    myOtherObject.InputStream.Seek(0, SeekOrigin.Begin);
    myOtherObject.InputStream.CopyTo(fileStream);
}

66
myOtherObject.InputStream.Seek(0, SeekOrigin.Begin)아직 시작하지 않았거나 전체 스트림을 복사하지 않으면 전화 해야합니다.
Steve Rukuts

3
이 입력 스트림이 http 연결에서 가져온 경우 버퍼링하고 다운로드 한 다음 소스에서 모든 바이트를 씁니까 ?????
dbw

2
스트림을 사용하고 스트림을 바인딩하고 동일한 스트림을 사용하여 pdf 파일을 저장하면 "Seek (0, SeekOrigin.Begin)"을 사용하지 않고 PDF 뷰어를 만들었습니다. 올바른 문서를 저장할 수 없습니다. "Seek (0, SeekOrigin.Begin)"이 (가) 언급 된 +1
user2463514

myOtherObject.InputStream.CopyTo (fileStream); 이 줄에 오류가 발생합니다 : 액세스가 거부되었습니다.
sulhadin

2
myOtherObject ??
Harry

531

당신은 사용 StreamReader(GIF를 또는 JPG를 같은) 바이너리 파일. StreamReader을위한 텍스트 데이터입니다. 임의의 이진 데이터에 사용하면 거의 확실하게 데이터가 손실됩니다. (Encoding.GetEncoding (28591)을 사용하면 괜찮을지 모르지만 요점은 무엇입니까?)

왜 전혀 사용해야 StreamReader합니까? 이진 데이터 이진 데이터 유지하고 이진 데이터 디스크 (또는 SQL)에 다시 쓰는 것이 어떻습니까?

편집 : 이것은 당신이 경우에 ... 사람들이보고 싶은 것을 보인다으로 않는 그냥이 같은 사용 무언가 (파일로 예를 들어) 다른 하나 개의 스트림을 복사하려면 :

/// <summary>
/// Copies the contents of input to output. Doesn't close either stream.
/// </summary>
public static void CopyStream(Stream input, Stream output)
{
    byte[] buffer = new byte[8 * 1024];
    int len;
    while ( (len = input.Read(buffer, 0, buffer.Length)) > 0)
    {
        output.Write(buffer, 0, len);
    }    
}

이를 사용하여 스트림을 파일로 덤프하려면 다음과 같이하십시오.

using (Stream file = File.Create(filename))
{
    CopyStream(input, file);
}

참고 Stream.CopyTo기본적으로 같은 목적을 제공, .NET 4 년에 도입되었다.


6
이것은 .NET에없는 것이 놀랍습니다. 사람들이 전체 파일의 크기로 바이트 배열을 만들면 큰 파일에 문제가 발생할 수 있습니다.
Tilendor

81
@Tilendor : .NET 4에서 확장 방법으로 제공됩니다. (CopyTo)
Jon Skeet

33
확장 방법이라고 생각하지 않지만 Stream 클래스의 새로운 기능입니다.
Kugel

9
@ Kugel : 당신이 옳아 요, 죄송합니다. 나는 유틸리티 라이브러리의 확장 방법으로 그것을했다,하지만 지금은 스트림 자체에 있다고, 내 확장 메서드가 호출되지 않습니다.
Jon Skeet

4
@Florian : 합리적으로 임의적입니다. 너무 많은 메모리를 사용하지 않을 정도로 작은 값이며, 한 번에 적당한 청크를 전송할 수있을만큼 큽니다. 16K, 32K이면 좋을 것입니다. 큰 객체 힙에 걸리지 않도록 조심할 것입니다.
Jon Skeet

77
public void CopyStream(Stream stream, string destPath)
{
  using (var fileStream = new FileStream(destPath, FileMode.Create, FileAccess.Write))
  {
    stream.CopyTo(fileStream);
  }
}

28
stream객체를 using(){}괄호 안에 넣지 않아야합니다 . 메소드가 스트림을 작성하지 않았으므로 처리하지 않아야합니다.
LarsTech

2
대신 FileStream사용 대신 넣어야 합니다. 그렇지 않으면 가비지 수집 될 때까지 계속 열려 있습니다.
Pavel Chikulaev 님이

AWS S3 클래스 게이트웨이 클래스를 사용하여 WinForms에서 내 문제를 해결하기 위해 귀하의 접근 방식이 훨씬 더 가깝다는 것을 알았습니다! 감사합니다!
Luiz Eduardo

2
이것은 잘 실행되었지만 0 KB 출력을 얻었습니다. 대신 올바른 출력을 위해이 작업을 수행해야했습니다 File.WriteAllBytes(destinationFilePath, input.ToArray());. 내 경우 input에는에서 MemoryStream오는 것입니다 ZipArchive.
SNag

23
private void SaveFileStream(String path, Stream stream)
{
    var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write);
    stream.CopyTo(fileStream);
    fileStream.Dispose();
}

1
이것은 잘 실행되었지만 0 KB 출력을 얻었습니다. 대신 올바른 출력을 위해이 작업을 수행해야했습니다 File.WriteAllBytes(destinationFilePath, input.ToArray());. 내 경우 input에는에서 MemoryStream오는 것입니다 ZipArchive.
SNag

2
이것은 내가 뭘 잘못하고 있는지 알아내는 데 도움이되었습니다. 그러나 개울의 시작 부분으로 이동하는 것을 잊지 마십시오. stream.Seek(0, SeekOrigin.Begin);
Nathan Bills

9

CopyTo앱을 사용하는 시스템이 .NET 4.0 이상으로 업그레이드되지 않았을 수있는을 사용하여 모든 답변을 얻지 못했습니다. 사람들이 사람들을 업그레이드하도록 강요하고 싶지만 호환성도 좋습니다.

또 다른 것은 스트림을 사용하여 처음에 다른 스트림에서 복사하지 않습니다. 왜 안 해?

byte[] bytes = myOtherObject.InputStream.ToArray();

바이트가 있으면 쉽게 파일에 쓸 수 있습니다.

public static void WriteFile(string fileName, byte[] bytes)
{
    string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
    if (!path.EndsWith(@"\")) path += @"\";

    if (File.Exists(Path.Combine(path, fileName)))
        File.Delete(Path.Combine(path, fileName));

    using (FileStream fs = new FileStream(Path.Combine(path, fileName), FileMode.CreateNew, FileAccess.Write))
    {
        fs.Write(bytes, 0, (int)bytes.Length);
        //fs.Close();
    }
}

이 코드는 .jpg파일로 테스트 한대로 작동 하지만 작은 파일 (1MB 미만)에서만 사용했음을 인정합니다. 하나의 스트림, 스트림 간 복사, 인코딩 불필요, 바이트 쓰기! 와 오버 복잡한 것들에 대한 필요가 없습니다 StreamReader당신은 이미 당신이 변환 할 수있는 스트림이있는 경우 bytes직접와를 .ToArray()!

이 방법으로 볼 수있는 잠재적 인 단점은 큰 파일이 있고 스트림으로 사용 .CopyTo()하고 동등한 FileStream것을 사용하면 바이트 배열을 사용하고 바이트를 하나씩 읽는 대신 스트리밍 할 수 있다는 것입니다. 결과적으로이 방법으로 수행하는 것이 느려질 수 있습니다. 그러나 바이트를 작성 .Write()하는 FileStream핸들 의 메소드가 한 번에 한 바이트 만 수행하므로 질식 해서는 안되므로 스트림을 a로 유지하기에 충분한 메모리가 있어야byte[] 한다는 것을 제외하고는 메모리를 방해하지 않습니다 . 객체 . 이것을 사용하고을 얻는 상황에서 OracleBlob에 가야했습니다. byte[]충분히 작아서 어쨌든 나에게 사용할 수있는 스트리밍이 없었기 때문에 위의 함수에 바이트를 보냈습니다.

스트림을 사용하는 또 다른 옵션 CopyStream은 다른 게시물에 있던 Jon Skeet의 기능 과 함께 사용 FileStream하는 것입니다. 입력 스트림을 가져 와서 직접 파일을 만드는 데 사용 됩니다. File.Create그가했던 것처럼을 사용하지 않습니다 (처음에는 나에게 문제가 된 것처럼 보였지만 나중에 VS 버그 일 가능성이 있음을 발견했습니다 ...).

/// <summary>
/// Copies the contents of input to output. Doesn't close either stream.
/// </summary>
public static void CopyStream(Stream input, Stream output)
{
    byte[] buffer = new byte[8 * 1024];
    int len;
    while ( (len = input.Read(buffer, 0, buffer.Length)) > 0)
    {
        output.Write(buffer, 0, len);
    }    
}

public static void WriteFile(string fileName, Stream inputStream)
{
    string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
    if (!path.EndsWith(@"\")) path += @"\";

    if (File.Exists(Path.Combine(path, fileName)))
        File.Delete(Path.Combine(path, fileName));

    using (FileStream fs = new FileStream(Path.Combine(path, fileName), FileMode.CreateNew, FileAccess.Write)
    {
        CopyStream(inputStream, fs);
    }

    inputStream.Close();
    inputStream.Flush();
}

1
Close때문에 전화 할 필요가 없습니다using()
Alex78191

@ Alex78191에 대해 이야기하고 있다면 inputStream.Close()다시 살펴보십시오 inputStream. 변수로 전송됩니다. 는 using온이고 path+filename출력 스트림. fs.Close()에서 중간에 대해 이야기하고 있다면 using죄송합니다.
vapcguy

8
//If you don't have .Net 4.0  :)

public void SaveStreamToFile(Stream stream, string filename)
{  
   using(Stream destination = File.Create(filename))
      Write(stream, destination);
}

//Typically I implement this Write method as a Stream extension method. 
//The framework handles buffering.

public void Write(Stream from, Stream to)
{
   for(int a = from.ReadByte(); a != -1; a = from.ReadByte())
      to.WriteByte( (byte) a );
}

/*
Note, StreamReader is an IEnumerable<Char> while Stream is an IEnumbable<byte>.
The distinction is significant such as in multiple byte character encodings 
like Unicode used in .Net where Char is one or more bytes (byte[n]). Also, the
resulting translation from IEnumerable<byte> to IEnumerable<Char> can loose bytes
or insert them (for example, "\n" vs. "\r\n") depending on the StreamReader instance
CurrentEncoding.
*/

16
바이트 단위로 스트림 복사 (ReadByte / WriteByte 사용)는 버퍼 별 버퍼 복사 (Read (byte [], int, int) / Write (byte [], int, int) 사용)보다 훨씬 느립니다.
Kevin

6

FileStream 객체를 사용하지 않는 이유는 무엇입니까?

public void SaveStreamToFile(string fileFullPath, Stream stream)
{
    if (stream.Length == 0) return;

    // Create a FileStream object to write a stream to a file
    using (FileStream fileStream = System.IO.File.Create(fileFullPath, (int)stream.Length))
    {
        // Fill the bytes[] array with the stream data
        byte[] bytesInStream = new byte[stream.Length];
        stream.Read(bytesInStream, 0, (int)bytesInStream.Length);

        // Use FileStream object to write to the specified file
        fileStream.Write(bytesInStream, 0, bytesInStream.Length);
     }
}

46
어떤 입력 스트림 긴 1기가바이트 경우 -이 코드는 : 1 기가 바이트 버퍼를 할당하려고 할 것
Buthrakaur

1
길이가 알 수 없기 때문에 ResponseStream과 함께 작동하지 않습니다.
Tomas Kubes

그것은 당신이 사용할 수있는 메모리를 가져야 byte[]한다는 것이 사실이지만, DVD 급류를 유지하는 사이트가 없다면 ... 요즘 대부분의 컴퓨터에는 요즘 사용 가능한 RAM이 2GB 이상 있습니다.주의 사항은 유효하지만 대부분의 작업에 "충분히 충분"하다고 생각합니다.
vapcguy

웹 서버는 한 번에 한 명의 사용자 만 활동하지 않는 한 이와 같은 경우를 전혀 용납하지 않습니다.
NateTheGreatt

6

다른 옵션은 스트림을 가져 와서 byte[]사용하는 것 File.WriteAllBytes입니다. 이것은해야합니다 :

using (var stream = new MemoryStream())
{
    input.CopyTo(stream);
    File.WriteAllBytes(file, stream.ToArray());
}

확장 방법으로 감싸면 이름이 더 좋아집니다.

public void WriteTo(this Stream input, string file)
{
    //your fav write method:

    using (var stream = File.Create(file))
    {
        input.CopyTo(stream);
    }

    //or

    using (var stream = new MemoryStream())
    {
        input.CopyTo(stream);
        File.WriteAllBytes(file, stream.ToArray());
    }

    //whatever that fits.
}

3
입력이 너무 크면 메모리 부족 예외가 발생합니다. 입력 스트림에서 파일 스트림으로 컨텐츠를 복사하는 옵션이 훨씬 좋습니다
Ykok

4
public void testdownload(stream input)
{
    byte[] buffer = new byte[16345];
    using (FileStream fs = new FileStream(this.FullLocalFilePath,
                        FileMode.Create, FileAccess.Write, FileShare.None))
    {
        int read;
        while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
        {
             fs.Write(buffer, 0, read);
        }
    }
}

버퍼링 된 입력 스트림을 직접 공급-nice FileStream!
vapcguy

3

다음은 idisposable의 올바른 사용법과 구현을 사용하는 예입니다.

static void WriteToFile(string sourceFile, string destinationfile, bool append = true, int bufferSize = 4096)
{
    using (var sourceFileStream = new FileStream(sourceFile, FileMode.OpenOrCreate))
    {
        using (var destinationFileStream = new FileStream(destinationfile, FileMode.OpenOrCreate))
        {
            while (sourceFileStream.Position < sourceFileStream.Length)
            {
                destinationFileStream.WriteByte((byte)sourceFileStream.ReadByte());
            }
        }
    }
}

... 그리고 이것도 있습니다

    public static void WriteToFile(FileStream stream, string destinationFile, int bufferSize = 4096, FileMode mode = FileMode.OpenOrCreate, FileAccess access = FileAccess.ReadWrite, FileShare share = FileShare.ReadWrite)
    {
        using (var destinationFileStream = new FileStream(destinationFile, mode, access, share))
        {
            while (stream.Position < stream.Length) 
            {
                destinationFileStream.WriteByte((byte)stream.ReadByte());
            }
        }
    }

핵심은 사용의 올바른 사용법 (위에 표시된 것과 같이 idisposable을 구현하는 객체의 인스턴스화에서 구현되어야 함)을 이해하고 속성이 스트림에 어떻게 작동하는지에 대한 좋은 아이디어를 갖는 것입니다. 위치는 문자 그대로 스트림 내의 인덱스 (0에서 시작)이며 readbyte 메소드를 사용하여 각 바이트를 읽을 때 따라옵니다. 이 경우 나는 본질적으로 for 루프 변수 대신에 그것을 사용하고 있으며 전체 스트림의 끝 부분 (바이트)에 이르는 길이까지 계속 따라갑니다. 실제로 동일하기 때문에 바이트 단위로 무시하면 모든 것을 깔끔하게 해결하는 이와 같이 간단하고 우아합니다.

또한 ReadByte 메서드는 단순히 프로세스에서 바이트를 int로 캐스팅하고 간단히 변환 할 수 있습니다.

최근에 작성한 다른 구현을 추가하여 대량의 과부하를 방지하기 위해 순차적 데이터 쓰기를 보장하기 위해 동적 버퍼를 작성합니다.

private void StreamBuffer(Stream stream, int buffer)
{
    using (var memoryStream = new MemoryStream())
    {
        stream.CopyTo(memoryStream);
        var memoryBuffer = memoryStream.GetBuffer();

        for (int i = 0; i < memoryBuffer.Length;)
        {
            var networkBuffer = new byte[buffer];
            for (int j = 0; j < networkBuffer.Length && i < memoryBuffer.Length; j++)
            {
                networkBuffer[j] = memoryBuffer[i];
                i++;
            }
            //Assuming destination file
            destinationFileStream.Write(networkBuffer, 0, networkBuffer.Length);
        }
    }
}

설명은 매우 간단합니다. 우리는 우리가 쓰고 자하는 전체 데이터 세트를 명심해야하며 또한 일정량 만 쓰려고한다는 것을 알고 있습니다. 따라서 마지막 매개 변수가 비어있는 첫 번째 루프를 원합니다. ). 다음으로 전달 된 크기로 설정된 바이트 배열 버퍼를 초기화하고 두 번째 루프를 사용하여 j를 버퍼 크기와 원래 크기와 비교하고 원래 크기보다 큰 경우 바이트 배열, 실행을 종료합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.