SaveBinaryDirect issues
-
07-01-2021 - |
Question
I have a little tool built in CSOM that backs up some pertinent documents by copying them to another site collection (don't ask). Sometimes, the program hangs until I close it as it gets stuck on some files. I'm not sure how to force the program's execution if this process takes too long.
Is there a way to terminate SaveBinaryDirect? Should I check for filesize before calling the method? How can I continue processing if the Save hangs for too long?
SP.FileInformation fileInfo = SP.File.OpenBinaryDirect(srcContext, f.ServerRelativeUrl);
SP.File.SaveBinaryDirect(destContext, nLocation, fileInfo.Stream, true);
Solution
One of the ways to set a timeout is to use the Task Parallel Library (TPL). You can wrap your call in a Task
and wait for it to complete based on either a time span or cancellation token.
Task upload = Task.Run(() =>
{
SP.FileInformation fileInfo = SP.File.OpenBinaryDirect(srcContext, f.ServerRelativeUrl);
SP.File.SaveBinaryDirect(destContext, nLocation, fileInfo.Stream, true);
});
if (!upload.Wait(TimeSpan.FromMilliseconds(1000)))
{
// handle upload failure
}
Another option is to use a more modern API. SaveBinaryDirect
was introduced in 2010 over WebDAV and has been superseded with better upload methods. SharePoint/PnP recommends using the ContentStream
property on FileCreationInformation
(for files less than 10MB) or StartUpload
, ContinueUpload
, and FinishUpload
on the File
class (for files 10MB or greater).
By using the continuation methods, the app can recover in the middle of a failure in the event of a timeout or cancellation.
OTHER TIPS
Here is a demo for your reference, and you can use this function directly.
/// <summary>
/// copy a file from a document library in a siteCollection to another document library in another siteCollection
/// </summary>
/// <param name="sourceSiteUrl">http://sp/sites/sourceSite</param>
/// <param name="destinationSiteUrl">http://sp/sites/destinationSite</param>
/// <param name="host">http://sp</param>
/// <param name="sourceListName"></param>
/// <param name="destinationListName"></param>
public static void CopyDocFromOneListToAnother(string sourceSiteUrl,string destinationSiteUrl, string host, string sourceListName, string destinationListName, string fileName)
{
sourceSiteUrl = sourceSiteUrl.EndsWith("/") ? sourceSiteUrl.Substring(0, sourceSiteUrl.Length - 1) : sourceSiteUrl;
destinationSiteUrl = destinationSiteUrl.EndsWith("/") ? destinationSiteUrl.Substring(0, destinationSiteUrl.Length - 1) : destinationSiteUrl;
ClientContext sourceContext = new ClientContext(sourceSiteUrl);
ClientContext destinationContext = new ClientContext(destinationSiteUrl);
Web sourceWeb = sourceContext.Site.RootWeb;
Web destinationWeb = destinationContext.Site.RootWeb;
List source = sourceWeb.Lists.GetByTitle(sourceListName);
List destination = destinationWeb.Lists.GetByTitle(destinationListName);
sourceContext.Load(source);
destinationContext.Load(destination);
sourceContext.Load(sourceWeb);
sourceContext.ExecuteQuery();
destinationContext.ExecuteQuery();
FileCollection files = source.RootFolder.Files;
Microsoft.SharePoint.Client.File file = files.GetByUrl(sourceSiteUrl+"/"+sourceListName+"/"+fileName);
sourceContext.Load(file);
sourceContext.ExecuteQuery();
FileInformation fileInfo = Microsoft.SharePoint.Client.File.OpenBinaryDirect(sourceContext, file.ServerRelativeUrl);
string filePath = host + file.ServerRelativeUrl;
System.IO.Stream fileStream = fileInfo.Stream;
FileCreationInformation createFile = new FileCreationInformation();
byte[] bufferByte = new byte[1024 * 100];
System.IO.MemoryStream memory = new System.IO.MemoryStream();
int len = 0;
while ((len = fileStream.Read(bufferByte, 0, bufferByte.Length)) > 0)
{
memory.Write(bufferByte, 0, len);
}
byte[] bytes = memory.GetBuffer();
createFile.Content = bytes;
createFile.Url = sourceSiteUrl + "/" + destinationListName + "/" + file.Name;
createFile.Overwrite = true;
Microsoft.SharePoint.Client.File newFile = destination.RootFolder.Files.Add(createFile);
newFile.ListItemAllFields.Update();
destinationContext.ExecuteQuery();
}