0

I'm trying to upload a PDF file to a CRM record. I've used a File type field in the entity that can hold my uploaded file. I've done this by using this code:

UploadBlockRequest blockRequest = new UploadBlockRequest();
blockRequest.BlockData = Convert.FromBase64String(documentBody);
blockRequest.BlockId = Convert.ToBase64String(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString()));
blockRequest.FileContinuationToken = initResponse.FileContinuationToken;

var blockResponse = (UploadBlockResponse)service.Execute(blockRequest);

It is working fine for PDF files that are under 4 MB. However, If I try to upload a PDF that's more than 4 MB, I get the following error:

Invalid file chunk size: 4 MB. Maximum chunk size supported: 4 MB.

Is there a way to upload large PDF files to CRM record?

using (var stream = new MemoryStream(Convert.FromBase64String(Base64)))
                    {
                        InitializeFileBlocksUploadRequest initializeUploadRequest = new InitializeFileBlocksUploadRequest();
                        initializeUploadRequest.FileAttributeName = "my_fileTypeField";
                        initializeUploadRequest.FileName = "Test.pdf";
                        initializeUploadRequest.Target = new EntityReference("my_entity", new Guid("my_guid"));

                        var initializeUploadResponse = (InitializeFileBlocksUploadResponse)service.Execute(initializeUploadRequest);
                        var uploadRequest = new UploadBlockRequest { FileContinuationToken = initializeUploadResponse.FileContinuationToken };

                        const int blockSize = 4194304; // 4MB
                        int byteCount;
                        var blockList = new List<string>();

                        do
                        {
                            
                            //uploadRequest.BlockData = Convert.FromBase64String(documentBody);
                            byteCount = stream.Read(uploadRequest.BlockData, 0, blockSize);
                            uploadRequest.BlockId = Convert.ToBase64String(Guid.NewGuid().ToByteArray());
                            service.Execute(uploadRequest);
                            blockList.Add(uploadRequest.BlockId);
                            Console.WriteLine(size + " == " + blockSize);
                        } while (size == blockSize);

                        var commitRequest = new CommitFileBlocksUploadRequest
                        {
                            BlockList = blockList.ToArray(),
                            FileContinuationToken = initializeUploadResponse.FileContinuationToken,
                            FileName = initializeUploadRequest.FileName,
                            MimeType = "application/pdf"
                        };
                        var commitResponse = (CommitFileBlocksUploadResponse)service.Execute(commitRequest);
                    }
3iL
  • 2,146
  • 2
  • 23
  • 47

1 Answers1

1

The UploadBlockRequest is one piece in the file upload procedure. You need 3 distinct requests:

  1. InitializeFileBlocksUploadRequest
  2. UploadBlockRequest
  3. CommitFileBlocksUploadRequest

The UploadBlockRequest can hold a chunk of 4 MB of data at a maximum. Your file can be as large as 128 MB and it can be uploaded using multiple upload requests.

A basic upload method could look like this:

private static Guid UploadFile
    (
        Stream stream,
        string fileName,
        string mimeType,
        EntityReference target,
        string fileAttributeName,
        IOrganizationService organizationService
    )
{
    var initializeUploadRequest = new InitializeFileBlocksUploadRequest
    {
        FileAttributeName = fileAttributeName,
        FileName = fileName,
        Target = target
    };

    var initializeUploadResponse = (InitializeFileBlocksUploadResponse)organizationService.Execute(initializeUploadRequest);

    const int blockSize = 4194304; // 4 MB
    int byteCount;
    var blockList = new List<string>();

    do
    {
        var buffer = new byte[blockSize];
        byteCount = stream.Read(buffer, 0, blockSize);

        if (byteCount < blockSize)
            Array.Resize(ref buffer, byteCount);

        var uploadRequest = new UploadBlockRequest
        {
            BlockData = buffer,
            BlockId = Convert.ToBase64String(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString("N"))),
            FileContinuationToken = initializeUploadResponse.FileContinuationToken
        };

        organizationService.Execute(uploadRequest);
        blockList.Add(uploadRequest.BlockId);
    } while (byteCount == blockSize);

    var commitRequest = new CommitFileBlocksUploadRequest
    {
        BlockList = blockList.ToArray(),
        FileContinuationToken = initializeUploadResponse.FileContinuationToken,
        FileName = initializeUploadRequest.FileName,
        MimeType = mimeType
    };

    var commitResponse = (CommitFileBlocksUploadResponse)organizationService.Execute(commitRequest);
    return commitResponse.FileId;
}

The method uploads the file and returns the ID of the file.

Henk van Boeijen
  • 7,357
  • 6
  • 32
  • 42
  • Thankyou for your answer. I am passing base64 string instead of file stream. In that case, how am I going to compare the condition in do- while? – 3iL Dec 23 '21 at 09:33
  • Create a `Stream` from the base64 string like this: `using (var stream = new MemoryStream(Convert.FromBase64String(documentBody)))` and feed the stream to the method. Of course you would need to change `FileStream` into `MemoryStream` or, even simpler, to the base class `Stream`. – Henk van Boeijen Dec 23 '21 at 11:37
  • Hi Henk van Boeijen , After implementing the above code I get the following exception: Exception caught - Buffer cannot be null. Parameter name: buffer. I'm updating my question so you can see my implementation. – 3iL Dec 28 '21 at 19:45
  • I am having the same issue as @3iL. – Svenisok Dec 30 '21 at 10:08
  • @3iL, I have reworked the example from Henk a little bit and got it working. However, I believe my approach wouldn't work for big files. – Svenisok Dec 30 '21 at 10:27
  • @Svenisok, I am dealing with files no larger than 32 MB. Can you please share your work? – 3iL Dec 30 '21 at 10:42
  • I'm sorry, I missed the fact that you explicityly were asking for +4MB files. I believe the missing part in the solution of @HenkvanBoeijen is a line in the Do... While putting the chunks in the BlockData property of the UploadBlockRequest. I haven't figured it out what the exact code would need to be. – Svenisok Dec 30 '21 at 12:58
  • 2
    I apologize for the sloppy code example. Should have tested it first. I fixed it and updated my answer. – Henk van Boeijen Dec 30 '21 at 16:58
  • @HenkvanBoeijen, your updated answer did the work for me. Thanks! – 3iL Jan 11 '22 at 17:15