How to Upload to SharePoint using a memory stream? Using COM - c#

I am creating a method to UploadDocument_FromStream() method which has one parameter -- Stream file.
I am having trouble trying to keep my SharePoint connection open to allow me to upload my Stream file to SharePoint. I think the issue is due to the fact that I am executing a query then trying to upload to SharePoint.
Is this the best way to handle Uploading to SharePoint with a MemoryStream?
UploadDocument_FromStream()
public void UploadDocument_FromStream(Stream file)
{
using (var clientContext = OpenConnectionToSharePoint())
{
if (file == null) throw new Exception("Stream cannot be null");
using (clientContext)
{
var list = clientContext.Web.Lists.GetByTitle("Documents");
clientContext.Load(list.RootFolder);
clientContext.ExecuteQuery();
Microsoft.SharePoint.Client.File.SaveBinaryDirect(clientContext, "/shared documents/test.pdf", file, true);
}
}
Also should note - that I am using SharePoint.Client.dll or COM approach.

SharePoint CSOM API utilizes internal query queue to support Request Batching via ClientRuntimeContext.Load method. But both File.OpenBinaryDirect and File.SaveBinaryDirect methods are executed directly and require internal queue to be empty. For that purpose you could utilize the following checking before invoking File.SaveBinaryDirect method:
if (ctx.HasPendingRequest)
ctx.ExecuteQuery();
The following example shows how to save a file stream into documents library
var list = ctx.Web.Lists.GetByTitle(listTitle);
ctx.Load(list.RootFolder);
ctx.ExecuteQuery();
var fileName = System.IO.Path.GetFileName(path);
byte[] data = System.IO.File.ReadAllBytes(path);
MemoryStream stream = new MemoryStream(data);
var fileUrl = Path.Combine(list.RootFolder.ServerRelativeUrl, fileName);
if (ctx.HasPendingRequest)
ctx.ExecuteQuery();
Microsoft.SharePoint.Client.File.SaveBinaryDirect(ctx, fileUrl, stream, true);

Related

Azure.Storage.Blog downloading file from Azure Storage container to local path - from ASP.NET Core - file path and mime type?

I have an Azure application built with ASP.NET Core using the MVC pattern. Document uploads are stored in Azure Blob Containers and the C# upload code I wrote is working great.
I am using Azure.Storage.Blobs version 12.14.1
Here is my download blob code:
//get document metadata stored in sql table, id comes from a method param
var document = _unitOfWork.Documents.GetById(id);
if (document == null)
{
throw new FileNotFoundException();
}
//get connection and container from appsettings.json
string connection = _appConfig.AzureStorageConnection;
string containerName = _appConfig.AzureStorageContainer;
//work with blob client
var serviceClient = new BlobServiceClient(connection);
var container = serviceClient.GetBlobContainerClient(containerName);
var fileName = document.UniqueDocumentName;
var blobClient = container.GetBlobClient(document.UniqueDocumentName);
using (FileStream fileStream = System.IO.File.OpenWrite("<path>"))
{
blobClient.DownloadTo(fileStream);
}
After I get to using code to set up the file stream, I don't understand what to pass into the OpenWrite method as a path. This application is a B2C app and so how do I just prompt a user to download the file?
I do get a file download with the above code but the file is called download.xml. That is not the file that should be downloaded. I expected the download file to be an .odt file.
Documentation seems to be very sparse on downloading from Azure Blob Storage
EDIT 1.
I got rid of the FileStream and did this instead:
MemoryStream ms = new MemoryStream();
ms.Position = 0;
blobClient.DownloadTo(ms);
return new FileStreamResult(ms, document.FileType);
I did this instead of using FileStream and returned FileResult instead:
MemoryStream ms = new MemoryStream();
ms.Position = 0;
blobClient.DownloadTo(ms);
return new FileStreamResult(ms, document.FileType);

Uploading Image into SharePoint document library C#

I have a web service that currently passes base64 image format, However, How can I insert it into a sharepoint library? should i convert it to bytes array ?
Yes, need to read image file stream and then use Microsoft.SharePoint.Client.File.SaveBinaryDirect to save into SharePoint library:
var fileName=#"C:\Test.jpg";
using (ClientContext context = new ClientContext("http://sp/"))
{
using (var fs = new FileStream(fileName, FileMode.Open))
{
var fi = new FileInfo(fileName);
var list = context.Web.Lists.GetByTitle("Documents");
context.Load(list.RootFolder);
context.ExecuteQuery();
var fileUrl = String.Format("{0}/{1}", list.RootFolder.ServerRelativeUrl, fi.Name);
Microsoft.SharePoint.Client.File.SaveBinaryDirect(context, fileUrl, fs, true);
}
}
Here is a similiar thread for your reference:
How to Upload Images to SharePoint Online(Office 365) Picture-library using CSOM (Java Script)?

How to get the stream for a Multipart file in webapi upload?

I need to upload a file using Stream (Azure Blobstorage), and just cannot find out how to get the stream from the object itself. See code below.
I'm new to the WebAPI and have used some examples. I'm getting the files and filedata, but it's not correct type for my methods to upload it. Therefore, I need to get or convert it into a normal Stream, which seems a bit hard at the moment :)
I know I need to use ReadAsStreamAsync().Result in some way, but it crashes in the foreach loop since I'm getting two provider.Contents (first one seems right, second one does not).
[System.Web.Http.HttpPost]
public async Task<HttpResponseMessage> Upload()
{
if (!Request.Content.IsMimeMultipartContent())
{
this.Request.CreateResponse(HttpStatusCode.UnsupportedMediaType);
}
var provider = GetMultipartProvider();
var result = await Request.Content.ReadAsMultipartAsync(provider);
// On upload, files are given a generic name like "BodyPart_26d6abe1-3ae1-416a-9429-b35f15e6e5d5"
// so this is how you can get the original file name
var originalFileName = GetDeserializedFileName(result.FileData.First());
// uploadedFileInfo object will give you some additional stuff like file length,
// creation time, directory name, a few filesystem methods etc..
var uploadedFileInfo = new FileInfo(result.FileData.First().LocalFileName);
// Remove this line as well as GetFormData method if you're not
// sending any form data with your upload request
var fileUploadObj = GetFormData<UploadDataModel>(result);
Stream filestream = null;
using (Stream stream = new MemoryStream())
{
foreach (HttpContent content in provider.Contents)
{
BinaryFormatter bFormatter = new BinaryFormatter();
bFormatter.Serialize(stream, content.ReadAsStreamAsync().Result);
stream.Position = 0;
filestream = stream;
}
}
var storage = new StorageServices();
storage.UploadBlob(filestream, originalFileName);**strong text**
private MultipartFormDataStreamProvider GetMultipartProvider()
{
var uploadFolder = "~/App_Data/Tmp/FileUploads"; // you could put this to web.config
var root = HttpContext.Current.Server.MapPath(uploadFolder);
Directory.CreateDirectory(root);
return new MultipartFormDataStreamProvider(root);
}
This is identical to a dilemma I had a few months ago (capturing the upload stream before the MultipartStreamProvider took over and auto-magically saved the stream to a file). The recommendation was to inherit that class and override the methods ... but that didn't work in my case. :( (I wanted the functionality of both the MultipartFileStreamProvider and MultipartFormDataStreamProvider rolled into one MultipartStreamProvider, without the autosave part).
This might help; here's one written by one of the Web API developers, and this from the same developer.
Hi just wanted to post my answer so if anybody encounters the same issue they can find a solution here itself.
here
MultipartMemoryStreamProvider stream = await this.Request.Content.ReadAsMultipartAsync();
foreach (var st in stream.Contents)
{
var fileBytes = await st.ReadAsByteArrayAsync();
string base64 = Convert.ToBase64String(fileBytes);
var contentHeader = st.Headers;
string filename = contentHeader.ContentDisposition.FileName.Replace("\"", "");
string filetype = contentHeader.ContentType.MediaType;
}
I used MultipartMemoryStreamProvider and got all the details like filename and filetype from the header of content.
Hope this helps someone.

Create SPListItem from ZipArchiveEntry without FileStream

I'm a new .Net developper and i'm facing an issue while developping an Uploaded Zip File in a document Library.
i need to extract the content of the Zip File uploaded to do some actions on the files contained in it.
So i choosed to use ZipArchive Stream to handle my problem, i can retrieve my SPFile from my DocLib easily and create the stream from it.
But i'm not able to create embedded files from ZipArchiveEntry, i tried the following piece of code ( not a copy/past, i'm not on my dev machine )
foreach(SPFile myFile in mySPFolder.Files)
{
ZipArchive myZip = new ZipArchive(myFile.OpenBinaryStream());
foreach(ZipArchiveEntry subZip in ZipArchive.Entries)
{
SPFile newFile = list.RootFolder.Add("myxml.xml",subZip.Open())
}
}
I'm facing an issue while creating my newFile as it's throwing me a System I/O error, as per my understanding it's maybe due to the fact that the stream returned by the method ZipArchiveEntry.Open() is a deflatestream.
I saw that the file creation can be done with a MemoryStream, but i'm not able to understand how to convert a deflatestream to a memorystream.
I got the solution from another place but..
In order to get a memorystream from a deflatestream you need to use the CopyTo() method from Stream.
public void ExtractLibraryZipFolder(SPWeb web, SPList myList, string FolderPath, SPFile myFile, bool overWrite)
{
ZipArchive myZip = new ZipArchive(myFile.OpenBinaryStream());
foreach (ZipArchiveEntry subZip in myZip.Entries)
{
MemoryStream myMemoryStream = new MemoryStream();
subZip.Open().CopyTo(myMemoryStream);
if (FolderPath != string.Empty)
{
SPFolder theFolder = web.GetFolder("/ImportToolLibrary/");
theFolder.SubFolders[FolderPath].Files.Add(subZip.Name, myMemoryStream);
}
else
{
SPFile myUpload = myList.RootFolder.Files.Add(subZip.Name, myMemoryStream);
}
}
}

When I use the .NET WebClient DownloadFileAsync I randomly get zero length files returned

I'm trying to download files from my FTP server - multiples at the same time. When i use the DownloadFileAsync .. random files are returned with a byte[] Length of 0. I can 100% confirm the file exists on the server and has content AND there FTP server (running Filezilla Server) isn't erroring and say's the file has been transferred.
private async Task<IList<FtpDataResult>> DownloadFileAsync(FtpFileName ftpFileName)
{
var address = new Uri(string.Format("ftp://{0}{1}", _server, ftpFileName.FullName));
var webClient = new WebClient
{
Credentials = new NetworkCredential(_username, _password)
};
var bytes = await webClient.DownloadDataTaskAsync(address);
using (var stream = new MemoryStream(bytes))
{
// extract the stream data (either files in a zip OR a file);
return result;
}
}
When I try this code, it's slower (of course) but all the files have content.
private async Task<IList<FtpDataResult>> DownloadFileAsync(FtpFileName ftpFileName)
{
var address = new Uri(string.Format("ftp://{0}{1}", _server, ftpFileName.FullName));
var webClient = new WebClient
{
Credentials = new NetworkCredential(_username, _password)
};
// NOTICE: I've removed the AWAIT and a different method.
var bytes = webClient.DownloadData(address);
using (var stream = new MemoryStream(bytes))
{
// extract the stream data (either files in a zip OR a file);
return result;
}
}
Can anyone see what I'm doing wrong, please? Why would the DownloadFileAsync be randomly returning zero bytes?
Try out FtpWebRequest/FtpWebResponse classes. You have more available to you for debugging purposes.
FtpWebRequest - http://msdn.microsoft.com/en-us/library/system.net.ftpwebrequest(v=vs.110).aspx
FtpWebResponse - http://msdn.microsoft.com/en-us/library/system.net.ftpwebresponse(v=vs.110).aspx
Take a look at http://netftp.codeplex.com/. It appears as though almost all methods implement IAsyncResult. There isn't much documentation on how to get started, but I would assume that it is similar to the synchronous FTP classes from the .NET framework. You can install the nuget package here: https://www.nuget.org/packages/System.Net.FtpClient/

Categories