0

I am using this code for uploading a file :

https://gist.github.com/bgrins/1789787

But if I am trying to use this code for uploading a file "2 GB" file I am getting out of memory exception and the reason in this line :

https://gist.github.com/bgrins/1789787#file-gistfile1-cs-L75

so how can I fix this issue?

Ebram
  • 1,042
  • 1
  • 13
  • 26
  • 32 bit or 64 bit system? Also what .NET framework version? – Trioj Jul 13 '18 at 18:46
  • 64 bit and .net 4.6.2 – Ebram Jul 13 '18 at 18:48
  • Try this: https://learn.microsoft.com/en-us/dotnet/framework/configure-apps/file-schema/runtime/gcallowverylargeobjects-element – Trioj Jul 13 '18 at 18:48
  • Your other potential alternatives involve using a different Stream implementation, such as MemoryTributary or RecyclableMemoryStream. Reality is you're likely rubbing up against the hard limit for size of an object. – Trioj Jul 13 '18 at 18:51
  • learn.microsoft.com/en-us/dotnet/framework/configure-apps/ this didn't work – Ebram Jul 13 '18 at 18:54
  • Just to be clear, the full link was to the gcAllowVeryLargeObjects app config flag. Are you saying that the link didn't work or the flag didn't work? Might also consult this post: https://stackoverflow.com/questions/1087982/single-objects-still-limited-to-2-gb-in-size-in-clr-4-0 -- If the flag isn't helping and an alternate stream implementation is annoying/not an option/not working, you will likely have to break the file into chunks. – Trioj Jul 13 '18 at 18:57

2 Answers2

2

Read giant file piece by piece, and upload pieces one by one. you could provide a progress bar also.

  1. upload code piece by piece : How to read a big file piece by piece in C#
  2. in server side, append new pieces to a file: C# Append byte array to existing file

you can detail the code with this idea. I did it once last year, but cannot share the code.

Dongdong
  • 2,208
  • 19
  • 28
  • yes, I understand. But the server side doesn't support appending to file. – Ebram Jul 13 '18 at 19:06
  • Could you please detail more about server side, I would update my answer. – Dongdong Jul 13 '18 at 19:29
  • Yes, The server side has API which is talking 3 parameters and the file data but we can't send chunks to this API because it will overwrite instead of appending to the file. – Ebram Jul 13 '18 at 19:58
  • if you don't care contents of giant files, and impossible to change server code, probably you need to upload your pieces as filename.piece.0001-filename.piece.xxx, it would cost least memory everywhere. but you need to know how many pieces files to download. – Dongdong Jul 13 '18 at 20:17
2

There are more than one solution

1- Writing to RequestStream directly instead of writing to MemoryStream :

https://blogs.msdn.microsoft.com/johan/2006/11/15/are-you-getting-outofmemoryexceptions-when-uploading-large-files/

 public static string MyUploader(string strFileToUpload, string strUrl)
{


string strFileFormName = "file";


Uri oUri = new Uri(strUrl);


string strBoundary = "----------" + DateTime.Now.Ticks.ToString("x");





// The trailing boundary string


byte[] boundaryBytes = Encoding.ASCII.GetBytes("\r\n--" + strBoundary + "\r\n");





// The post message header


StringBuilder sb = new StringBuilder();


sb.Append("--");


sb.Append(strBoundary);


sb.Append("\r\n");


sb.Append("Content-Disposition: form-data; name=\"");


sb.Append(strFileFormName);


sb.Append("\"; filename=\"");


sb.Append(Path.GetFileName(strFileToUpload));


sb.Append("\"");


sb.Append("\r\n");


sb.Append("Content-Type: ");


sb.Append("application/octet-stream");


sb.Append("\r\n");


sb.Append("\r\n");


string strPostHeader = sb.ToString();


byte[] postHeaderBytes = Encoding.UTF8.GetBytes(strPostHeader);





// The WebRequest


HttpWebRequest oWebrequest = (HttpWebRequest)WebRequest.Create(oUri);


oWebrequest.ContentType = "multipart/form-data; boundary=" + strBoundary;


oWebrequest.Method = "POST";





// This is important, otherwise the whole file will be read to memory anyway...


oWebrequest.AllowWriteStreamBuffering = false;





// Get a FileStream and set the final properties of the WebRequest


FileStream oFileStream = new FileStream(strFileToUpload, FileMode.Open, FileAccess.Read);


long length = postHeaderBytes.Length + oFileStream.Length + boundaryBytes.Length;


oWebrequest.ContentLength = length;


Stream oRequestStream = oWebrequest.GetRequestStream();





// Write the post header


oRequestStream.Write(postHeaderBytes, 0, postHeaderBytes.Length);





// Stream the file contents in small pieces (4096 bytes, max).


byte[] buffer = new Byte[checked((uint)Math.Min(4096, (int)oFileStream.Length))];


int bytesRead = 0;


while ((bytesRead = oFileStream.Read(buffer, 0, buffer.Length)) != 0)


    oRequestStream.Write(buffer, 0, bytesRead);


oFileStream.Close();





// Add the trailing boundary


oRequestStream.Write(boundaryBytes, 0, boundaryBytes.Length);


WebResponse oWResponse = oWebrequest.GetResponse();


Stream s = oWResponse.GetResponseStream();


StreamReader sr = new StreamReader(s);


String sReturnString = sr.ReadToEnd();





// Clean up


oFileStream.Close();


oRequestStream.Close();


s.Close();


sr.Close();





return sReturnString;

}

2- Using RecyclableMemoryStream instead of MemoryStream solution

You can read more about RecyclableMemoryStream here : http://www.philosophicalgeek.com/2015/02/06/announcing-microsoft-io-recycablememorystream/

https://github.com/Microsoft/Microsoft.IO.RecyclableMemoryStream

3- Using MemoryTributary instead of MemoryStream

You can read more about MemoryTributary here :

https://www.codeproject.com/Articles/348590/A-replacement-for-MemoryStream?msg=5257615#xx5257615xx

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Runtime.InteropServices;

   namespace LiquidEngine.Tools
       {
/// <summary>
/// MemoryTributary is a re-implementation of MemoryStream that uses a dynamic list of byte arrays as a backing store, instead of a single byte array, the allocation
/// of which will fail for relatively small streams as it requires contiguous memory.
/// </summary>
public class MemoryTributary : Stream       /* http://msdn.microsoft.com/en-us/library/system.io.stream.aspx */
{
    #region Constructors

    public MemoryTributary()
    {
        Position = 0;
    }

    public MemoryTributary(byte[] source)
    {
        this.Write(source, 0, source.Length);
        Position = 0;
    }

    /* length is ignored because capacity has no meaning unless we implement an artifical limit */
    public MemoryTributary(int length)
    {
        SetLength(length);
        Position = length;
        byte[] d = block;   //access block to prompt the allocation of memory
        Position = 0;
    }

    #endregion

    #region Status Properties

    public override bool CanRead
    {
        get { return true; }
    }

    public override bool CanSeek
    {
        get { return true; }
    }

    public override bool CanWrite
    {
        get { return true; }
    }

    #endregion

    #region Public Properties

    public override long Length
    {
        get { return length; }
    }

    public override long Position { get; set; }

    #endregion

    #region Members

    protected long length = 0;

    protected long blockSize = 65536;

    protected List<byte[]> blocks = new List<byte[]>();

    #endregion

    #region Internal Properties

    /* Use these properties to gain access to the appropriate block of memory for the current Position */

    /// <summary>
    /// The block of memory currently addressed by Position
    /// </summary>
    protected byte[] block
    {
        get
        {
            while (blocks.Count <= blockId)
                blocks.Add(new byte[blockSize]);
            return blocks[(int)blockId];
        }
    }
    /// <summary>
    /// The id of the block currently addressed by Position
    /// </summary>
    protected long blockId
    {
        get { return Position / blockSize; }
    }
    /// <summary>
    /// The offset of the byte currently addressed by Position, into the block that contains it
    /// </summary>
    protected long blockOffset
    {
        get { return Position % blockSize; }
    }

    #endregion

    #region Public Stream Methods

    public override void Flush()
    {
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        long lcount = (long)count;

        if (lcount < 0)
        {
            throw new ArgumentOutOfRangeException("count", lcount, "Number of bytes to copy cannot be negative.");
        }

        long remaining = (length - Position);
        if (lcount > remaining)
            lcount = remaining;

        if (buffer == null)
        {
            throw new ArgumentNullException("buffer", "Buffer cannot be null.");
        }
        if (offset < 0)
        {
            throw new ArgumentOutOfRangeException("offset",offset,"Destination offset cannot be negative.");
        }

        int read = 0;
        long copysize = 0;
        do
        {
            copysize = Math.Min(lcount, (blockSize - blockOffset));
            Buffer.BlockCopy(block, (int)blockOffset, buffer, offset, (int)copysize);
            lcount -= copysize;
            offset += (int)copysize;

            read += (int)copysize;
            Position += copysize;

        } while (lcount > 0);

        return read;

    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        switch (origin)
        {
            case SeekOrigin.Begin:
                Position = offset;
                break;
            case SeekOrigin.Current:
                Position += offset;
                break;
            case SeekOrigin.End:
                Position = Length - offset;
                break;
        }
        return Position;
    }

    public override void SetLength(long value)
    {
        length = value;
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        long initialPosition = Position;
        int copysize;
        try
        {
            do
            {
                copysize = Math.Min(count, (int)(blockSize - blockOffset));

                EnsureCapacity(Position + copysize);

                Buffer.BlockCopy(buffer, (int)offset, block, (int)blockOffset, copysize);
                count -= copysize;
                offset += copysize;

                Position += copysize;

            } while (count > 0);
        }
        catch (Exception e)
        {
            Position = initialPosition;
            throw e;
        }
    }

    public override int ReadByte()
    {
        if (Position >= length)
            return -1;

        byte b = block[blockOffset];
        Position++;

        return b;
    }

    public override void WriteByte(byte value)
    {
        EnsureCapacity(Position + 1);
        block[blockOffset] = value;
        Position++;
    }

    protected void EnsureCapacity(long intended_length)
    {
        if (intended_length > length)
            length = (intended_length);
    }

    #endregion

    #region IDispose

    /* http://msdn.microsoft.com/en-us/library/fs2xkftw.aspx */
    protected override void Dispose(bool disposing)
    {
        /* We do not currently use unmanaged resources */
        base.Dispose(disposing);
    }

    #endregion

    #region Public Additional Helper Methods

    /// <summary>
    /// Returns the entire content of the stream as a byte array. This is not safe because the call to new byte[] may 
    /// fail if the stream is large enough. Where possible use methods which operate on streams directly instead.
    /// </summary>
    /// <returns>A byte[] containing the current data in the stream</returns>
    public byte[] ToArray()
    {
        long firstposition = Position;
        Position = 0;
        byte[] destination = new byte[Length];
        Read(destination, 0, (int)Length);
        Position = firstposition;
        return destination;
    }

    /// <summary>
    /// Reads length bytes from source into the this instance at the current position.
    /// </summary>
    /// <param name="source">The stream containing the data to copy</param>
    /// <param name="length">The number of bytes to copy</param>
    public void ReadFrom(Stream source, long length)
    {
        byte[] buffer = new byte[4096];
        int read;
        do
        {
            read = source.Read(buffer, 0, (int)Math.Min(4096, length));
            length -= read;
            this.Write(buffer, 0, read);

        } while (length > 0);
    }

    /// <summary>
    /// Writes the entire stream into destination, regardless of Position, which remains unchanged.
    /// </summary>
    /// <param name="destination">The stream to write the content of this stream to</param>
    public void WriteTo(Stream destination)
    {
        long initialpos = Position;
        Position = 0;
        this.CopyTo(destination);
        Position = initialpos;
    }

    #endregion
}
 }
Ebram
  • 1,042
  • 1
  • 13
  • 26