MemoryStream Uploading Empty File to SharePoint in C# MVC Project - c#

I am testing out the ability to upload a file to SharePoint. I am using the following code:
var targetSiteURL = #"https://company.sharepoint.com/sites/ProjectRoom";
var login = "user#company.com";
var password = "PWD123!";
var securePassword = new SecureString();
foreach (var c in password)
{
securePassword.AppendChar(c);
}
var ctx = new ClientContext(targetSiteURL)
{
Credentials = new SharePointOnlineCredentials(login, securePassword)
};
var fileName = vm.UploadedFile.FileName;
using (var target = new MemoryStream())
{
vm.UploadedFile.InputStream.CopyTo(target);
Microsoft.SharePoint.Client.File.SaveBinaryDirect(ctx, $"/sites/ProjectRoom/Project Room Test/{fileName}", target, true);
}
A file is uploaded with the correct name to the correct location. However, the file is blank.
UploadedFile is a property in my ViewModel =>
public HttpPostedFileBase UploadedFile { get; set; }
I also have an input type of file named UploadedFile in my form, which I use to select the file to upload =>
<input type="file" name="UploadedFile" id="UploadedFile"/>
My question is: Why is the file that I upload blank?

A MemoryStream has an internal pointer to the next byte to read. After inserting bytes into it, you need to reset it back to the start or the consuming app may not realise and will read it as empty. To reset it, just set the Position property back to zero:
vm.UploadedFile.InputStream.CopyTo(target);
// Add this line:
target.Position = 0;
Microsoft.SharePoint.Client.File.SaveBinaryDirect(....);

Related

Download all documents from SharePoint List using the URL

I wanted to know how to download all documents from a SharePoint list using the SharePoint client object model (CSOM) (Microsoft.SharePoint.Client) and the lists full URL.
For example, if the URL was http://teamhub.myorg.local/sites/teams/it/ISLibrary/Guides/
Is it possible to connect directly to that URL and retrieve all documents stored there?
I have tried out the below code but I am getting an error, also it seems to require that I split the URL into two parts.
string baseURL = "http://teamhub.myorg.local/sites/";
string listURL = "teams/it/ISLibrary/Guides/";
var ctx = new ClientContext(baseURL);
ctx.Credentials = new SharePointOnlineCredentials(userName, SecuredpassWord);
var list = ctx.Web.GetList(listURL);
ctx.Load(list);
ctx.ExecuteQuery();
Console.WriteLine(list.Title);
When I run this code I simply get a "File not found" error.
Can it be done by simply passing in the full url somewhere?
I will need to do this connection and get all documents 100's of times over for many different lists, so it would be best if there is a way to do it using the full URL.
Any advice is appreciated. Thanks
Microsoft.SharePoint.Client.Web.GetListByUrl use webRelativeUrl, for example:
My site: https://tenant.sharepoint.com/sites/TST, library: https://tenant.sharepoint.com/sites/TST/MyDoc4
So the code would be:
Web web = clientContext.Web;
var lib=web.GetListByUrl("/MyDoc4");
The listURL you shared seems a folder, so we could get the folder and files in folder as below:
Web web = clientContext.Web;
Folder folder = web.GetFolderByServerRelativeUrl("/sites/TST/MyDoc4/Folder");
var files = folder.Files;
clientContext.Load(files);
clientContext.ExecuteQuery();
Download fileļ¼š
foreach (var file in files)
{
clientContext.Load(file);
Console.WriteLine(file.Name);
ClientResult<Stream> stream = file.OpenBinaryStream();
clientContext.ExecuteQuery();
var fileOut = Path.Combine(localPath, file.Name);
if (!System.IO.File.Exists(fileOut))
{
using (Stream fileStream = new FileStream(fileOut, FileMode.Create))
{
CopyStream(stream.Value, fileStream);
}
}
}
private static void CopyStream(Stream src, Stream dest)
{
byte[] buf = new byte[8192];
for (; ; )
{
int numRead = src.Read(buf, 0, buf.Length);
if (numRead == 0)
break;
dest.Write(buf, 0, numRead);
}
}

Storing and getting back files from MongoDB

I am working on c# .Net 4.5
I have to upload some files on MongoDB and in other module, I have to get them back based on metadata.
for that I am doing like below,
static void uploadFileToMongoDB(GridFSBucket gridFsBucket)
{
if (Directory.Exists(_sourceFilePath))
{
if (!Directory.Exists(_uploadedFilePath))
Directory.CreateDirectory(_uploadedFilePath);
FileInfo[] sourceFileInfo = new DirectoryInfo(_sourceFilePath).GetFiles();
foreach (FileInfo fileInfo in sourceFileInfo)
{
string filePath = fileInfo.FullName;
string remoteFileName = fileInfo.Name;
string extension = Path.GetExtension(filePath);
double fileCreationDate = fileInfo.CreationTime.ToOADate();
GridFSUploadOptions gridUploadOption = new GridFSUploadOptions
{
Metadata = new BsonDocument
{{ "creationDate", fileCreationDate },
{ "extension", extension }}
};
using (Stream fileStream = File.OpenRead(filePath))
gridFsBucket.UploadFromStream(remoteFileName, fileStream, gridUploadOption);
}
}
}
and downloading,
static void getFileInfoFromMongoDB(GridFSBucket bucket, DateTime startDate, DateTime endDate)
{
double startDateDoube = startDate.ToOADate();
double endDateDouble = endDate.ToOADate();
var filter = Builders<GridFSFileInfo>.Filter.And(
Builders<GridFSFileInfo>.Filter.Gt(x => x.Metadata["creationDate"], startDateDoube),
Builders<GridFSFileInfo>.Filter.Lt(x => x.Metadata["creationDate"], endDateDouble));
IAsyncCursor<GridFSFileInfo> fileInfoList = bucket.Find(filter); //****
if (!Directory.Exists(_destFilePath))
Directory.CreateDirectory(_destFilePath);
foreach (GridFSFileInfo fileInfo in fileInfoList.ToList())
{
string destFile = _destFilePath + "\\" + fileInfo.Filename;
var fileContent = bucket.DownloadAsBytes(fileInfo.Id); //****
File.WriteAllBytes(destFile, fileContent);
}
}
in this code (working but) I have two problems which I am not sure how to fix.
If i have uploaded a file and I upload it again, it actually gets
uploaded. How to prevent it?
Ofcourse both uploaded files have different ObjectId but while uploading a file I will not be knowing that which files are already uploaded. So I want a mechanism which throws an exception if i upload already uploaded file. Is it possible? (I can use combination of filename, created date, etc)
If you have noticed in code, actually i am requesting to database server twice to get one file written on disk, How to do it in one shot?
Note lines of code which I have marked with "//****" comment. First I am querying into database to get fileInfo (GridFSFileInfo). I was expecting that I could get actual content of file from this objects only. But I didnot find any related property or method in that object. so I had to do var fileContent = bucket.DownloadAsBytes(fileInfo.Id); to get content. M I missing something basic here ?

File locked after stream.CopyTo .netCore2

I am using JSReport in order to generate reports in .netCore2; In the below method the view is returned to the user and report is saved in specified directory;
public IActionResult ImageDownload()
{
HttpContext.JsReportFeature().Recipe(Recipe.PhantomPdf)
.Configure((r) => r.Template.Phantom = new Phantom
{
Format = PhantomFormat.A4,
Orientation = PhantomOrientation.Portrait
}).OnAfterRender( (r) =>
{
var streamIo = r.Content; // streamIo is of type System.IO
streamIo.CopyTo(System.IO.File.OpenWrite("C:GeneratedReports\\myReport.pdf"));
streamIo.Seek(0, SeekOrigin.Begin);
}
);
var dp = new Classes.DataProvider();
var lstnames = dp.GetRegisteredNames();
var lst = lstnames.ToArray<string>();
return View("Users", lst);
}
Once the view is returned a new browser opens displaying the pdf report. Also the same pdf will be saved in the webserver in the given directory; The problem is that the created report in the directory seems to be locked, I cannot copy it, open it... unless I close the .net solution. Any explanation of what's happening here?
You FileStream isn't closed when OnAfterRender has completed meaning no other app can open/access it. Try changing the code to put a using block around the File.OpenWrite call to contain a using statement for the FileStream e.g.
public IActionResult ImageDownload()
{
HttpContext.JsReportFeature().Recipe(Recipe.PhantomPdf)
.Configure((r) => r.Template.Phantom = new Phantom
{
Format = PhantomFormat.A4,
Orientation = PhantomOrientation.Portrait
}).OnAfterRender( (r) =>
{
var streamIo = r.Content; // streamIo is of type System.IO
using(var fs = System.IO.File.OpenWrite("C:GeneratedReports\\myReport.pdf"))
{
streamIo.CopyTo(fs);
}
streamIo.Seek(0, SeekOrigin.Begin);
}
);
var dp = new Classes.DataProvider();
var lstnames = dp.GetRegisteredNames();
var lst = lstnames.ToArray<string>();
return View("Users", lst);
}

This method or property is not supported after HttpRequest.GetBufferlessInputStream has been invoked

I am trying to browse and upload a file from client to server using Angular Js and WEB API.I used Input file type for user to select file and post the file to WEB API. In web API, I am getting following error "This method or property is not supported after HttpRequest.GetBufferlessInputStream has been invoked."
I am using the following code:-
public IHttpActionResult UploadForm()
{
HttpResponseMessage response = new HttpResponseMessage();
var httpRequest = System.Web.HttpContext.Current.Request;
if (httpRequest.Files.Count > 0)
{
foreach (string file in httpRequest.Files)
{
var postedFile = httpRequest.Files[file];
var filePath = System.Web.HttpContext.Current.Server.MapPath("~/UploadFile/" + postedFile.FileName);
postedFile.SaveAs(filePath);
}
}
return Json("Document Saved");
}
I get this error when i tried to get files from HTTP request... should I update anything in web config??
Please help me to resolve this issue..
try this it work fine for me.
//get the root folder where file will be store
string root = HttpContext.Current.Server.MapPath("~/UploadFile");
// Read the form data.
var provider = new MultipartFormDataStreamProvider(root);
await Request.Content.ReadAsMultipartAsync(provider);
if (provider.FileData.Count > 0 && provider.FileData[0] != null)
{
MultipartFileData file = provider.FileData[0];
//clean the file name
var fileWithoutQuote = file.Headers.ContentDisposition.FileName.Substring(1, file.Headers.ContentDisposition.FileName.Length - 2);
//get current file directory on the server
var directory = Path.GetDirectoryName(file.LocalFileName);
if (directory != null)
{
//generate new random file name (not mandatory)
var randomFileName = Path.Combine(directory, Path.GetRandomFileName());
var fileExtension = Path.GetExtension(fileWithoutQuote);
var newfilename = Path.ChangeExtension(randomFileName, fileExtension);
//Move file to rename existing upload file name with new random filr name
File.Move(file.LocalFileName, newfilename);
}
}
I also had the same problem. And the solution by #Jean did not work for me.
I need to upload some CSV file and had to use it in the controller.
In Javascript, I used Fetch API to upload the csv file.
But, in the controller, I used this code:
[HttpPost]
[CatchException]
public bool ImportBundlesFromCsv()
{
var a = Request.Content.ReadAsByteArrayAsync();
//convert to Stream if needed
Stream stream = new MemoryStream(a.Result); // a.Result is byte[]
// convert to String if needed
string result = System.Text.Encoding.UTF8.GetString(a.Result);
// your code
return true;
}
This worked for me. Hope this helps!

How to tell if Azure Container has Virtual folders/directories?

I am working on a project that is reading the data from a Azure Blob and saving that data into an Object. I am currently running into a problem. The way my code is set up now - it will read all the .txt data within a container if there are no Virtual Folders present.
However, if there is a virtual folder structure present within a Azure Container
my code will error out, with a NullExceptionReference. My idea was to do a if check to see if there was Virtual Folders present within an Azure Container if so execute //some code. Is there a way to tell if there is a virtual folder is present?
ReturnBlobObject()
private List<Blob> ReturnBlobObject(O365 o365)
{
List<Blob> listResult = new List<Blob>();
string textToFindPattern = "(\\/)";
string fileName = null;
string content = null;
//Loop through all Blobs and split the container form the file name.
foreach (var blobItem in o365.Container.ListBlobs(useFlatBlobListing: true))
{
string containerAndFileName = blobItem.Parent.Uri.MakeRelativeUri(blobItem.Uri).ToString();
string[] subString = Regex.Split(containerAndFileName, textToFindPattern);
//subString[2] is the name of the file.
fileName = subString[2];
content = ReadFromBlobStream(o365.Container.GetBlobReference(subString[2]));
Blob blobObject = new Blob(fileName, content);
listResult.Add(blobObject);
}
return listResult;
}
ReadFromBlobStream
private string ReadFromBlobStream(CloudBlob blob)
{
Stream stream = blob.OpenRead();
using (StreamReader reader = new StreamReader(stream))
{
return reader.ReadToEnd();
}
}
I was able to solve this by refactoring my code. Instead of using Regex - which was returning some very odd behavior I decided to take a step back and think the problem. Below is the solution I came up with.
ReturnBlobObject()
private List<Blob> ReturnBlobObject(O365 o365)
{
List<Blob> listResult = new List<Blob>();
//Loop through all Blobs and split the container form the file name.
foreach (var blobItem in o365.Container.ListBlobs(useFlatBlobListing: true))
{
string fileName = blobItem.Uri.LocalPath.Replace(string.Format("/{0}/", o365.Container.Name), "");
string content = ReadFromBlobStream(o365.Container.GetBlobReference(fileName));
Blob blobObject = new Blob(fileName, content);
listResult.Add(blobObject);
}
return listResult;
}

Categories