2

I am trying to download files from a SharePoint library using the client object model. I seem to be able to access the files using OpenBinaryStream() and then executing the query, but when I try to access the stream, it is a stream of Length = 0. I've seen many examples and I've tried several, but I can't get the files to download. I've uploaded successfully, and credentials and permissions aren't the problem. Anyone have any thoughts?

public SharepointFileContainer DownloadFolder(bool includeSubfolders, params object[] path)
    {
        try
        {
            List<string> pathStrings = new List<string>();
            foreach (object o in path)
                pathStrings.Add(o.ToString());

            var docs = _context.Web.Lists.GetByTitle(Library);
            _context.Load(docs);
            _context.ExecuteQuery();
            var rootFolder = docs.RootFolder;
            _context.Load(rootFolder);
            _context.ExecuteQuery();
            var folder = GetFolder(rootFolder, pathStrings);
            var files = folder.Files;
            _context.Load(files);
            _context.ExecuteQuery();

            SharepointFileContainer remoteFiles = new SharepointFileContainer();

            foreach (Sharepoint.File f in files)
            {
                _context.Load(f);
                var file = f.OpenBinaryStream();

                _context.ExecuteQuery();

                var memoryStream = new MemoryStream();

                file.Value.CopyTo(memoryStream);
                remoteFiles.Files.Add(f.Name, memoryStream);
            }
...
}

SharepointFileContainer is just a custom class for my calling application to dispose of the streams when it has finished processing them. GetFolder is a recursive method to drill down the given folder path. I've had problems with providing the direct url and have had the most success with this.

My big question is why "file.Value" is a Stream with a Length == 0?

Thanks in advance!

EDIT: Thanks for your input so far...unfortunately I'm experiencing the same problem. Both solutions pitched make use of OpenBinaryDirect. The resulting FileInformation class has this for the stream...

enter image description here

I'm still getting a file with 0 bytes downloaded.

Crash5998
  • 336
  • 5
  • 12

2 Answers2

3

You need to get the list item of the file (as a ListItem object) and then use it's property File. Something like:

//...
// Previous code
//...
 var docs = _context.Web.Lists.GetByTitle(Library);
 var listItem = docs.GetItemById(listItemId);
 _context.Load(docs);
 clientContext.Load(listItem, i => i.File);
 clientContext.ExecuteQuery();

 var fileRef = listItem.File.ServerRelativeUrl;
 var fileInfo = Microsoft.SharePoint.Client.File.OpenBinaryDirect(clientContext, fileRef);
 var fileName = Path.Combine(filePath,(string)listItem.File.Name);
 using (var fileStream = System.IO.File.Create(fileName))
 {                  
      fileInfo.Stream.CopyTo(fileStream);
 }

After that you do whatever you need to do with the stream. The current one just saves it to the specified path, but you can also download it in the browser, etc..

m3n7alsnak3
  • 3,026
  • 1
  • 15
  • 24
  • 1
    Thank you both for your assistance...we later discovered that the problem wasn't with the code, but with the version of the CSOM I was attempting to use. I'm sure both solutions work, but I'm selecting this one since it is a bit cleaner. – Crash5998 Jan 29 '18 at 17:55
  • @Crash5998, can you describe how exactly you changed the CSOM version? I am currently facing the exact same problem trying to download files from Sharepoint 2016 server, and get the exception "This property cannot be set after writing has started". Any hints would be appreciated, thanks – Erik Feb 15 '21 at 08:55
1

We can use the following code to get the memory stream.

var fileInformation = Microsoft.SharePoint.Client.File.OpenBinaryDirect(clientContext, file.ServerRelativeUrl);

if (fileInformation != null && fileInformation.Stream != null)
{
    using (MemoryStream memoryStream = new MemoryStream())
    {                
        byte[] buffer = new byte[32768];
        int bytesRead;
        do
        {
            bytesRead = fileInformation.Stream.Read(buffer, 0, buffer.Length);
            memoryStream.Write(buffer, 0, bytesRead);
        } while (bytesRead != 0);
    }
}

Reference: https://praveenkasireddy.wordpress.com/2012/11/11/download-document-from-document-set-using-client-object-model-om/

LZ_MSFT
  • 4,079
  • 1
  • 8
  • 9