मैं सी # में एक फ़ाइल के लिए एक स्ट्रीम कैसे बचा सकता हूं?


713

मेरे पास एक StreamReaderऑब्जेक्ट है जिसे मैंने एक स्ट्रीम के साथ आरंभीकृत किया है, अब मैं इस स्ट्रीम को डिस्क पर सहेजना चाहता हूं (स्ट्रीम एक .gifया .jpgया हो सकता है .pdf)।

मौजूदा कोड:

StreamReader sr = new StreamReader(myOtherObject.InputStream);
  1. मुझे इसे डिस्क पर सहेजने की आवश्यकता है (मेरे पास फ़ाइल नाम है)।
  2. भविष्य में मैं इसे SQL सर्वर पर संग्रहीत करना चाहता हूँ।

मेरे पास एन्कोडिंग प्रकार भी है, जिसे मुझे SQL सर्वर पर संग्रहीत करने की आवश्यकता होगी, सही?


1
MyOtherObject क्या है?
anhtv13

2
फिर भी इस सवाल पर कोई स्वीकृत जवाब नहीं?
ब्रेट रिग्बी

@BrettRigby एक जॉन स्कीट उत्तर है, यह बहुत अधिक स्वतः स्वीकृत है: D
रिकार्डो डायस मोरिस

जवाबों:


912

जॉन स्कीट के जवाब में तिलेंडर द्वारा हाइलाइट किए जाने के 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)यदि आप पहले से ही शुरू नहीं कर रहे हैं या आप पूरी स्ट्रीम को कॉपी नहीं करेंगे।
स्टीव रुक्ट्स

3
अगर यह इनपुट स्ट्रीम http कनेक्शन से मिली है तो क्या यह बफर और डाउनलोड करेगा और फिर सोर्स से सभी बाइट्स लिखेगा ?????
dbw

2
मैंने पीडीएफ दर्शक बनाया है जहां मैं स्ट्रीम का उपयोग कर रहा हूं, एक बार मैं स्ट्रीम को बांधता हूं और जब मैं एक ही स्ट्रीम का उपयोग करके पीडीएफ फाइल को सहेजता हूं तो "सीक (0, सीकॉरिगिन.बेगिन)" का उपयोग किए बिना मैं सही दस्तावेज को बचाने में सक्षम नहीं होगा। तो इस "सीक (0, सीकॉरिजिन.बेगिन)" का उल्लेख करने के लिए +1
user2463514

myOtherObject.InputStream.CopyTo (FileStream); यह पंक्ति एक त्रुटि देती है: एक्सेस अस्वीकृत।
सुलहदीन

2
myOtherObject ??
हैरी

531

आपको बाइनरी फ़ाइलों (जैसे gif या jpgs) के लिए उपयोग नहीं करना चाहिएStreamReaderStreamReaderके लिए है पाठ डेटा। यदि आप मनमाने ढंग से बाइनरी डेटा के लिए इसका उपयोग करते हैं, तो आप लगभग निश्चित रूप से डेटा खो देंगे। (यदि आप Encoding.GetEncoding (28591) का उपयोग करते हैं तो आप शायद ठीक हो जाएंगे, लेकिन बात क्या है?)

आपको बिल्कुल उपयोग करने की आवश्यकता क्यों है StreamReader? केवल बाइनरी डेटा को बाइनरी डेटा के रूप में क्यों नहीं रखा जाता है और इसे बाइनरी डेटा के रूप में डिस्क (या एसक्यूएल) पर वापस लिखता है?

संपादित करें: जैसा कि ऐसा प्रतीत होता है कि कुछ लोग देखना चाहते हैं ... यदि आप बस एक स्ट्रीम को दूसरी (जैसे फ़ाइल में) कॉपी करना चाहते हैं तो इस तरह से कुछ का उपयोग करें:

/// <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 में नहीं है। मैं देखता हूं कि बाइट बनाने वाले लोग पूरी फाइल का आकार बनाते हैं, जिससे बड़ी फ़ाइलों के लिए समस्या हो सकती है।
तिलेंडर

81
@ टिलर: यह .NET 4 में एक विस्तार विधि के रूप में मौजूद है (CopyTo)
जॉन स्कीट

33
मुझे नहीं लगता कि यह एक विस्तार विधि है, लेकिन स्ट्रीम क्लास में यह नया है।
कुगेल

9
@Kugel: आप सही हैं, क्षमा करें। मेरे पास एक उपयोगिता पुस्तकालय में एक विस्तार विधि के रूप में था, लेकिन अब जब यह स्ट्रीम में ही है, तो मेरी एक्सटेंशन विधि को कॉल नहीं किया जाता है।
जॉन स्कीट

4
@ फ़्लोरियन: यह उचित रूप से मनमाना है - बहुत अधिक स्मृति लेने से बचने के लिए एक छोटा सा पर्याप्त मूल्य, और एक समय में एक उचित चंक को स्थानांतरित करने के लिए पर्याप्त बड़ा। यह ठीक होगा 16K, 32K हो सकता है - मैं बस बड़े ऑब्जेक्ट के ढेर पर समाप्त नहीं करने के लिए सावधान रहना होगा।
जॉन स्कीट

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

28
आपको संभवतः streamऑब्जेक्ट को using(){}ब्रैकेट में नहीं रखना चाहिए । आपकी विधि ने धारा नहीं बनाई, इसलिए इसे इसका निपटान नहीं करना चाहिए।
लार्सटेक

2
इसके बजाय आपको FileStreamउपयोग करने के बजाय डालने की आवश्यकता है , अन्यथा इसे तब तक खुला रखा जाएगा जब तक कि यह कचरा एकत्र न हो जाए।
पावेल चिकुलाव

मैंने पाया है कि आपका दृष्टिकोण मेरी AWS S3 वर्ग गेटवे क्लास के साथ WinForms में मेरी समस्या को हल करने के बहुत करीब था! धन्यवाद!
लुइज़ एडुआर्डो

2
यह ठीक चला लेकिन मुझे 0 केबी आउटपुट मिला। इसके बजाय मुझे सही आउटपुट के लिए ऐसा करना पड़ा 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 केबी आउटपुट मिला। इसके बजाय मुझे सही आउटपुट के लिए ऐसा करना पड़ा File.WriteAllBytes(destinationFilePath, input.ToArray());:। मेरे मामले में, inputएक के MemoryStreamभीतर से आ रहा है ZipArchive
SNag

2
इससे मुझे यह पता लगाने में मदद मिली कि मैं क्या गलत कर रहा था। हालाँकि, स्ट्रीम की शुरुआत में जाना न भूलें: stream.Seek(0, SeekOrigin.Begin);
नाथन बिल्स

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फ़ाइल के साथ परीक्षण किया है , हालांकि मैं मानता हूं कि मैंने इसे केवल छोटी फ़ाइलों (1 एमबी से कम) के साथ उपयोग किया है। एक धारा, धाराओं के बीच कोई प्रतिलिपि नहीं, कोई एन्कोडिंग की आवश्यकता नहीं है, बस बाइट्स लिखें! StreamReaderयदि आपके पास पहले से ही एक स्ट्रीम है जिसके साथ आप bytesसीधे सीधे रूपांतरित हो सकते हैं, तो अधिक जटिल चीज़ों की ज़रूरत नहीं है .ToArray()!

केवल संभावित डाउनसाइड्स मैं इसे इस तरह से कर सकता हूं यदि आपके पास एक बड़ी फाइल है, तो इसे स्ट्रीम के रूप में रखना और बाइट सरणी का उपयोग करने और एक-एक करके बाइट्स पढ़ने के बजाय इसे स्ट्रीम करने .CopyTo()या समतुल्य करने की अनुमति देता FileStreamहै। यह धीमी गति से ऐसा कर सकता है, परिणामस्वरूप। लेकिन बाइट्स को लिखने .Write()वाले FileStreamहैंडल की विधि के बाद से इसे चोक नहीं करना चाहिए , और यह केवल एक बार में एक बाइट कर रहा है, इसलिए यह मेमोरी को रोक नहीं पाएगा, सिवाय इसके कि आपके पास स्ट्रीम के रूप में स्ट्रीम रखने के लिए पर्याप्त मेमोरी होनी चाहिए byte[]वस्तु । मेरी स्थिति में जहां मैंने इसका इस्तेमाल किया, एक होने के नाते OracleBlob, मुझे एक में जाना पड़ा byte[], यह काफी छोटा था, और इसके अलावा, मेरे लिए कोई स्ट्रीमिंग उपलब्ध नहीं थी, वैसे भी, इसलिए मैंने बस अपने फाइट्स को अपने फंक्शन में भेजा, ऊपर।

एक अन्य विकल्प, एक स्ट्रीम का उपयोग करते हुए, इसे जॉन स्कीट के CopyStreamफ़ंक्शन के साथ उपयोग करना होगा जो कि किसी अन्य पोस्ट में था - यह सिर्फ FileStreamइनपुट स्ट्रीम लेने के लिए और सीधे से फाइल बनाने के लिए उपयोग करता है। यह उपयोग नहीं करता है File.Create, जैसे उसने किया था (जो शुरू में मेरे लिए समस्याग्रस्त लग रहा था, लेकिन बाद में पाया गया कि यह संभवतः एक वीएस बग ...) था।

/// <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
Closeusing()
एलेक्स78191

@ एलेक्स78191 यदि आप बात कर रहे हैं inputStream.Close(), तो फिर से देखें - inputStreamएक चर के रूप में भेजा जाता है। usingपर है path+filenameउत्पादन धारा। यदि आप fs.Close()के बीच में बात कर रहे थे using, क्षमा करें, आप उसके बारे में सही थे और मैंने उसे हटा दिया।
वाप्गुगी

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 का उपयोग करके) कॉपी करना बफर-बाय-बफर (रीड (बाइट [], int, int) / Write (बाइट [], int, int) का उपयोग करके कॉपी करने की तुलना में बहुत धीमा होगा)।
केविन

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
क्या होगा यदि इनपुट स्ट्रीम 1GB लंबी है - यह कोड 1GB बफर को आवंटित करने की कोशिश करेगा :)
Buthrakaur

1
यह ResponseStream के साथ काम नहीं कर रहा है, क्योंकि यह uknown लंबाई का है।
टॉमस क्यूब्स

हालांकि यह सच है कि आपके पास इसके लिए उपलब्ध मेमोरी byte[]होनी चाहिए, मुझे लगता है कि यह दुर्लभ होगा कि आप किसी फ़ाइल में 1 जीबी + बूँद जमा कर रहे हों ... जब तक कि आपके पास एक ऐसी साइट न हो जो डीवीडी टॉरेंट रखती हो ... प्लस , अधिकांश कंप्यूटरों में इन दिनों कम से कम 2 जीबी रैम उपलब्ध है, वैसे भी .... कैविट मान्य है, लेकिन मुझे लगता है कि यह एक ऐसा मामला है जहां यह संभवतः सबसे अच्छा है "" सबसे अधिक नौकरियों के लिए।
वाप्गुगी

Webservers इस तरह के एक मामले को बिल्कुल भी बर्दाश्त नहीं करेंगे, जब तक कि वेबसाइट पर केवल एक ही उपयोगकर्ता सक्रिय न हो।
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
यदि इनपुट बहुत बड़ा है, तो आपको मेमोरी अपवाद नहीं मिलेगा। इनपुट स्ट्रीम से कंटेंट को कॉपी करने का विकल्प ज्यादा बेहतर है
योकू

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);
        }
    }
}

एक बफर इनपुट धारा की आपूर्ति सीधे FileStream- अच्छा!
vapcguy

3

यहां एक उदाहरण दिया गया है जो उचित उपयोग और आइडिसोपायरी के कार्यान्वयन का उपयोग करता है:

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());
            }
        }
    }

कुंजी का उपयोग करने के उचित उपयोग को समझ रहा है (जिसे उस वस्तु के तात्कालिकता पर लागू किया जाना चाहिए जो ऊपर दिखाए गए अनुसार आइडिपोसेबल को लागू करता है), और एक अच्छा विचार है कि कैसे स्ट्रीम के लिए गुण काम करते हैं। स्थिति का शाब्दिक अर्थ है कि धारा के भीतर सूचकांक (जो 0 से शुरू होता है) जिसे प्रत्येक बाइट के रूप में पालन किया जाता है, जिसे रीडबाइट विधि का उपयोग करके पढ़ा जाता है। इस मामले में मैं अनिवार्य रूप से लूप वेरिएबल के स्थान पर इसका उपयोग कर रहा हूं और बस इसे लंबाई तक सभी तरह से पालन करने देता हूं जो कि पूरी धारा के अंत में (बाइट्स में) है। बाइट्स में ध्यान न दें क्योंकि यह व्यावहारिक रूप से समान है और आपके पास कुछ इस तरह सरल और सुरुचिपूर्ण होगा जो सब कुछ साफ-सुथरा रूप से हल करता है।

ध्यान रखें, यह भी पढ़ें कि रीडबाइट विधि प्रक्रिया में एक बाइट को केवल एक इंट में डालती है और बस वापस परिवर्तित किया जा सकता है।

मैं एक और कार्यान्वयन जोड़ने जा रहा हूं मैंने हाल ही में बड़े पैमाने पर अधिभार को रोकने के लिए अनुक्रमिक डेटा लिखने के लिए एक गतिशील बफर बनाने के लिए लिखा था

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);
        }
    }
}

स्पष्टीकरण काफी सरल है: हम जानते हैं कि हमें उस डेटा के पूरे सेट को ध्यान में रखना होगा जिसे हम लिखना चाहते हैं और यह भी कि हम केवल कुछ निश्चित मात्राएँ लिखना चाहते हैं, इसलिए हम अंतिम पैरामीटर के साथ पहला लूप चाहते हैं (जैसे ही) )। अगला, हम एक बाइट सरणी बफ़र को आरंभीकृत करते हैं जो कि पारित होने के आकार पर सेट होता है, और दूसरे लूप के साथ हम जे की तुलना बफर के आकार और मूल एक के आकार से करते हैं, और यदि यह मूल के आकार से अधिक है बाइट सरणी, रन समाप्त करें।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.