MultipartMemoryStreamProvider: filename? - c#

I already asked here how I can read uploaded files in Web Api without the need to save them.
This question was answered with the MultipartMemoryStreamProvider, but how do I get the file name with this method to derive the type of the uploaded file from it?
Kind regards

There is an example in this DotNetNuke Code here (See the PostFile() method).
Updated based on #FilipW comment...
Get the content item you require and then access the filename property.
Something like this :
var provider = new MultipartMemoryStreamProvider();
var task = request.Content.ReadAsMultipartAsync(provider).
ContinueWith(o =>
{
//Select the appropriate content item this assumes only 1 part
var fileContent = provider.Contents.SingleOrDefault();
if (fileContent != null)
{
var fileName = fileContent.Headers.ContentDisposition.FileName.Replace("\"", string.Empty);
}
});//Ending Bracket

Related

How to properly attach a bulk of small files to a task in graph api

Have the following code.
if (attachments != null)
{
if (attachments.Length > 0)
{
_newTask.Attachments = new TodoTaskAttachmentsCollectionPage();
foreach (var _attachment in attachments)
{
_newTask.Attachments.Add(new TaskFileAttachment
{
Name = _attachment.FileName,
ContentBytes = _attachment.ContentBytes,
ContentType = _attachment.ContentType
});
}
}
}
await _graphServiceClient.Users[idUser].Todo.Lists[idList].Tasks.Request().AddAsync(_newTask);
Im trying to add multiple small files to a task and then post it to the graph api.
But it results in the following error:
One or more errors occurred. (One or more errors occurred. (A type
named 'microsoft.toDo.taskFileAttachment' could not be resolved by the
model. When a model is available, each type name must resolve to a
valid type.
Basically saying that the type taskFileAttachment is not the correct type to add to the collection of attachments of a task.
But, according to MSdoc that's the correct type to add.
Cant see what i'm missing and there is not a lot of documentation of how to post small files to a task. I already done it through the api for mails and thought it was really straightforward but it looks as it is not the case.
As per the docs , there are list of property that are required when you create the todoTask , in that you can't find any property to attach the file
First try to create a new listItems , and attach the file
Sample to attach the file
GraphServiceClient graphClient = new GraphServiceClient( authProvider );
var attachmentBase = new TaskFileAttachment
{
Name = "smile",
ContentBytes = Convert.FromBase64String("a0b1c76de9f7="),
ContentType = "image/gif"
};
await graphClient.Me.Todo.Lists["{todoTaskList-id}"].Tasks["{todoTask-id}"].Attachments
.Request()
.AddAsync(attachmentBase);
So as they commented in my original post, there is no current way to attach a collection of files in one single request. They must attached to a created task one at a time.

Logic issue when saving URLs to file

What my goal with my piece of code is not to save duplicate domains to a .txt file if a checkbox is ticked.
Code:
// save to file here
if (footprints.Any(externalUrl.Contains))
{
// Load all URLs into an array ...
var hash = new List<string>(File.ReadAllLines(#"Links\" + lblFootprintsUsed.Text));
// Find the domain root url e.g. site.com ...
var root = Helpers.GetRootUrl(externalUrl);
if (chkBoxDoNotSaveDuplicateDomains.Checked == true)
{
if (!hash.Contains(Helpers.GetRootUrl(externalUrl)))
{
using (var sr = new StreamWriter(#"Links\" + lblFootprintsUsed.Text, true))
{
// before saving make & to & and get rid of #038; altogether ...
var newURL = externalUrl.Replace("&", "&").Replace("#038;", " ");
sr.WriteLine(newURL);
footprintsCount++;
}
}
}
if (chkBoxDoNotSaveDuplicateDomains.Checked == false)
{
if (!hash.Contains(externalUrl))
{
using (var sr = new StreamWriter(#"Links\" + lblFootprintsUsed.Text, true))
{
// before saving make & to & and get rid of #038; altogether ...
var newURL = externalUrl.Replace("&", "&").Replace("#038;", " ");
sr.WriteLine(newURL);
footprintsCount++;
}
}
}
}
The code above starts off by checking if a certain footprint pattern is found in a URL structure, if it does we load all URLs into a List the way !hash.Contains(externalUrl) should work is NOT to add duplicate URLs to the .txt file, but i can see from testing it does add complete duplicate URLs (the first issue) i never noticed this before, then i tried to add !hash.Contains(Helpers.GetRootUrl(externalUrl)) which should not add duplicate domains to the .txt file.
So unchecked, the code should not add duplicate URLs to file.
And checked the code should not add duplicate domains to file.
Both seem to fail, i cannot see any issue in the code as such, is there anyhting i am missing or could do better? any help is appreciated.
Here you are adding the full URL to the file, but while checking you are comparing only with the root URL
Modify the condition
if (!hash.Contains(Helpers.GetRootUrl(externalUrl)))
to
if (!hash.Any(x => x.Contains(Helpers.GetRootUrl(externalUrl))))

Returning file from ASP.NET Controller [duplicate]

I'm encountering a problem sending files stored in a database back to the user in ASP.NET MVC. What I want is a view listing two links, one to view the file and let the mimetype sent to the browser determine how it should be handled, and the other to force a download.
If I choose to view a file called SomeRandomFile.bak and the browser doesn't have an associated program to open files of this type, then I have no problem with it defaulting to the download behavior. However, if I choose to view a file called SomeRandomFile.pdf or SomeRandomFile.jpg I want the file to simply open. But I also want to keep a download link off to the side so that I can force a download prompt regardless of the file type. Does this make sense?
I have tried FileStreamResult and it works for most files, its constructor doesn't accept a filename by default, so unknown files are assigned a file name based on the URL (which does not know the extension to give based on content type). If I force the file name by specifying it, I lose the ability for the browser to open the file directly and I get a download prompt. Has anyone else encountered this?
These are the examples of what I've tried so far.
//Gives me a download prompt.
return File(document.Data, document.ContentType, document.Name);
//Opens if it is a known extension type, downloads otherwise (download has bogus name and missing extension)
return new FileStreamResult(new MemoryStream(document.Data), document.ContentType);
//Gives me a download prompt (lose the ability to open by default if known type)
return new FileStreamResult(new MemoryStream(document.Data), document.ContentType) {FileDownloadName = document.Name};
Any suggestions?
UPDATE:
This questions seems to strike a chord with a lot of people, so I thought I'd post an update. The warning on the accepted answer below that was added by Oskar regarding international characters is completely valid, and I've hit it a few times due to using the ContentDisposition class. I've since updated my implementation to fix this. While the code below is from my most recent incarnation of this problem in an ASP.NET Core (Full Framework) app, it should work with minimal changes in an older MVC application as well since I'm using the System.Net.Http.Headers.ContentDispositionHeaderValue class.
using System.Net.Http.Headers;
public IActionResult Download()
{
Document document = ... //Obtain document from database context
//"attachment" means always prompt the user to download
//"inline" means let the browser try and handle it
var cd = new ContentDispositionHeaderValue("attachment")
{
FileNameStar = document.FileName
};
Response.Headers.Add(HeaderNames.ContentDisposition, cd.ToString());
return File(document.Data, document.ContentType);
}
// an entity class for the document in my database
public class Document
{
public string FileName { get; set; }
public string ContentType { get; set; }
public byte[] Data { get; set; }
//Other properties left out for brevity
}
public ActionResult Download()
{
var document = ...
var cd = new System.Net.Mime.ContentDisposition
{
// for example foo.bak
FileName = document.FileName,
// always prompt the user for downloading, set to true if you want
// the browser to try to show the file inline
Inline = false,
};
Response.AppendHeader("Content-Disposition", cd.ToString());
return File(document.Data, document.ContentType);
}
NOTE: This example code above fails to properly account for international characters in the filename. See RFC6266 for the relevant standardization. I believe recent versions of ASP.Net MVC's File() method and the ContentDispositionHeaderValue class properly accounts for this. - Oskar 2016-02-25
I had trouble with the accepted answer due to no type hinting on the "document" variable: var document = ... So I'm posting what worked for me as an alternative in case anybody else is having trouble.
public ActionResult DownloadFile()
{
string filename = "File.pdf";
string filepath = AppDomain.CurrentDomain.BaseDirectory + "/Path/To/File/" + filename;
byte[] filedata = System.IO.File.ReadAllBytes(filepath);
string contentType = MimeMapping.GetMimeMapping(filepath);
var cd = new System.Net.Mime.ContentDisposition
{
FileName = filename,
Inline = true,
};
Response.AppendHeader("Content-Disposition", cd.ToString());
return File(filedata, contentType);
}
To view file (txt for example):
return File("~/TextFileInRootDir.txt", MediaTypeNames.Text.Plain);
To download file (txt for example):
return File("~/TextFileInRootDir.txt", MediaTypeNames.Text.Plain, "TextFile.txt");
note: to download file we should pass fileDownloadName argument
Darin Dimitrov's answer is correct. Just an addition:
Response.AppendHeader("Content-Disposition", cd.ToString()); may cause the browser to fail rendering the file if your response already contains a "Content-Disposition" header. In that case, you may want to use:
Response.Headers.Add("Content-Disposition", cd.ToString());
I believe this answer is cleaner, (based on
https://stackoverflow.com/a/3007668/550975)
public ActionResult GetAttachment(long id)
{
FileAttachment attachment;
using (var db = new TheContext())
{
attachment = db.FileAttachments.FirstOrDefault(x => x.Id == id);
}
return File(attachment.FileData, "application/force-download", Path.GetFileName(attachment.FileName));
}
Below code worked for me for getting a pdf file from an API service and response it out to the browser - hope it helps;
public async Task<FileResult> PrintPdfStatements(string fileName)
{
var fileContent = await GetFileStreamAsync(fileName);
var fileContentBytes = ((MemoryStream)fileContent).ToArray();
return File(fileContentBytes, System.Net.Mime.MediaTypeNames.Application.Pdf);
}
FileVirtualPath --> Research\Global Office Review.pdf
public virtual ActionResult GetFile()
{
return File(FileVirtualPath, "application/force-download", Path.GetFileName(FileVirtualPath));
}
Action method needs to return FileResult with either a stream, byte[], or virtual path of the file. You will also need to know the content-type of the file being downloaded. Here is a sample (quick/dirty) utility method. Sample video link
How to download files using asp.net core
[Route("api/[controller]")]
public class DownloadController : Controller
{
[HttpGet]
public async Task<IActionResult> Download()
{
var path = #"C:\Vetrivel\winforms.png";
var memory = new MemoryStream();
using (var stream = new FileStream(path, FileMode.Open))
{
await stream.CopyToAsync(memory);
}
memory.Position = 0;
var ext = Path.GetExtension(path).ToLowerInvariant();
return File(memory, GetMimeTypes()[ext], Path.GetFileName(path));
}
private Dictionary<string, string> GetMimeTypes()
{
return new Dictionary<string, string>
{
{".txt", "text/plain"},
{".pdf", "application/pdf"},
{".doc", "application/vnd.ms-word"},
{".docx", "application/vnd.ms-word"},
{".png", "image/png"},
{".jpg", "image/jpeg"},
...
};
}
}
If, like me, you've come to this topic via Razor components as you're learning Blazor, then you'll find you need to think a little more outside of the box to solve this problem. It's a bit of a minefield if (also like me) Blazor is your first forray into the MVC-type world, as the documentation isn't as helpful for such 'menial' tasks.
So, at the time of writing, you cannot achieve this using vanilla Blazor/Razor without embedding an MVC controller to handle the file download part an example of which is as below:
using Microsoft.AspNetCore.Mvc;
using Microsoft.Net.Http.Headers;
[Route("api/[controller]")]
[ApiController]
public class FileHandlingController : ControllerBase
{
[HttpGet]
public FileContentResult Download(int attachmentId)
{
TaskAttachment taskFile = null;
if (attachmentId > 0)
{
// taskFile = <your code to get the file>
// which assumes it's an object with relevant properties as required below
if (taskFile != null)
{
var cd = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
{
FileNameStar = taskFile.Filename
};
Response.Headers.Add(HeaderNames.ContentDisposition, cd.ToString());
}
}
return new FileContentResult(taskFile?.FileData, taskFile?.FileContentType);
}
}
Next, make sure your application startup (Startup.cs) is configured to correctly use MVC and has the following line present (add it if not):
services.AddMvc();
.. and then finally modify your component to link to the controller, for example (iterative based example using a custom class):
<tbody>
#foreach (var attachment in yourAttachments)
{
<tr>
<td>#attachment.Filename </td>
<td>#attachment.CreatedUser</td>
<td>#attachment.Created?.ToString("dd MMM yyyy")</td>
<td><ul><li class="oi oi-circle-x delete-attachment"></li></ul></td>
</tr>
}
</tbody>
Hopefully this helps anyone who struggled (like me!) to get an appropriate answer to this seemingly simple question in the realms of Blazor…!

GetGeotagAsync fails on files from a list created via Folder.CreateFileQueryWithOptions w/ OrderByName

Objective: get a list of image files from a user specified folder and retrieve the latitude/longitude metadata for each file if available.
The approach is to use Folder query with options to return a list of files which are then iterated over to get the geotag info. As a simple test I just added some code to the Geotag sample provided by MS; ChooseFolderButton_Click method added. It uses FolderPicker then simply picks the first file from the query (just as a test) and treats it like the original demo does to display geotag info.
The problem appears to be with how the StorageFile items are returned from the query AND only when CommonFileQuery.OrderByName is used. If DefaultQuery is used then all works.
Here is the code added to MS Samples Geotag (a button was added to the XAML):
private async void ChooseFolderButton_Click()
{
FolderPicker picker = new FolderPicker
{
SuggestedStartLocation = PickerLocationId.PicturesLibrary,
CommitButtonText = "Select",
ViewMode = PickerViewMode.Thumbnail,
};
picker.FileTypeFilter.Add("*");
StorageFolder Folder = await picker.PickSingleFolderAsync();
if (Folder != null) {
// Get the files and sort them myself
IReadOnlyList<StorageFile> files = await Folder.GetFilesAsync();
List<StorageFile> sortedList = files.Where(f => string.Compare(f.FileType,".jpg", ignoreCase: true) == 0 )
.OrderBy(f => f.DisplayName)
.Select(f => f)
.ToList();
file = sortedList.FirstOrDefault();
}
if (Folder != null) {
// Use Folder GetFiles with query options to get sorted list
var queryOptions = new QueryOptions(CommonFileQuery.DefaultQuery, new List<string> { ".jpg" })
{
FolderDepth = FolderDepth.Shallow
};
//queryOptions.SetPropertyPrefetch(PropertyPrefetchOptions.ImageProperties, null);
IReadOnlyList<StorageFile> files2 = await Folder.CreateFileQueryWithOptions(queryOptions).GetFilesAsync();
var file2 = files2[0];
}
if (Folder != null && file != null)
{
FileDisplayName.Text = file.DisplayName;
FileOperationsPanel.Visibility = Visibility.Visible;
}
else
{
FileOperationsPanel.Visibility = Visibility.Collapsed;
}
}
Selecting the file from sortedList works fine. Using the result from files2 will also work when DefaultQuery is used as shown. Change it to OrderByName and the GetGeotagAsync call will fail, returning null.
Looking into it in detail, it appears the StorageFile instance returned is different in this last case. The FolderRelativeId has the file extension duplicated; numbers\filename.ext.ext ; however, access through the StorageFile instance seems to work otherwise... except for GetGeotagAsync() at a minimum. This makes me wonder if a copy of the files are being created and the metadata is not included.
I am relatively new to C# and UWP and this is my first question post so I hope this is enough detail... The Question is basically: Am I missing something here? Doing something wrong in the Folder Query? I can work around it using linq as I did in the example code, or even just use DefaultQuery; yet this is uncomfortable as I do use the folder query approach in several other things. Does query OrderByName duplicate or do something undesireable?
My assumption is that those acquired files will be displayed to the ui,
if this is the case, you can use the nifty AdvancedCollectionView that is part of Windows Community Toolkit.
AdvancedCollectionView allows for a persistent sorting as well as filtering of contained items based on their properties, a proccess that makes it incredibly usefull for modern binding intensive apps.
Here is an example:
List<StorageFile> FileList = New List<StorageFile>();
Microsoft.Toolkit.Uwp.UI.AdvancedCollectionView ObservableFileList
{
get; set;
}
void InitializeList(){
//here we pass the backing list as an argument,
//any changes on the filelist will be directly reflected on our new observablelist, and vice versa
ObservableFileList = new Microsoft.Toolkit.Uwp.UI.AdvancedCollectionView(FileList);
//here we add sorting definitions,
//"DisplayName" is the current property we choose to sort against
ObservableFileList.SortDescriptions.Add(new SortDescription("DisplayName",SortDirection.Descending));
}
void AcquireNewDataSet(){
//GetFiles should return your files with no particular order.
List<StorageFile>tmp = GetFiles();
//always prefer ReplaceRange
FileList.ReplaceRange(tmp);
}
following this process, your observable list will always be alphabetically sorted, even when using it as ItemSource on various UI Item Containers

How do I get a previous version of a file with libgit2sharp

I'm trying to use libgit2sharp to get a previous version of a file. I would prefer the working directory to remain as is, at the very least restored to previous condition.
My initial approach was to try to stash, checkout path on the file I want, save that to a string variable, then stash pop. Is there a way to stash pop? I can't find it easily. Here's the code I have so far:
using (var repo = new Repository(DirectoryPath, null))
{
var currentCommit = repo.Head.Tip.Sha;
var commit = repo.Commits.Where(c => c.Sha == commitHash).FirstOrDefault();
if (commit == null)
return null;
var sn = "Stash Name";
var now = new DateTimeOffset(DateTime.Now);
var diffCount = repo.Diff.Compare().Count();
if(diffCount > 0)
repo.Stashes.Add(new Signature(sn, "x#y.com", now), options: StashModifiers.Default);
repo.CheckoutPaths(commit.Sha, new List<string>{ path }, CheckoutModifiers.None, null, null);
var fileText = File.ReadAllText(path);
repo.CheckoutPaths(currentCommit, new List<string>{path}, CheckoutModifiers.None, null, null);
if(diffCount > 0)
; // stash Pop?
}
If there's an easier approach than using Stash, that would work great also.
Is there a way to stash pop? I can't find it easily
Unfortunately, Stash pop requires merging which isn't available yet in libgit2.
I'm trying to use libgit2sharp to get a previous version of a file. I would prefer the working directory to remain as is
You may achieve such result by opening two instances of the same repository, each of them pointing to different working directories. The Repository constructor accepts a RepositoryOptions parameter which should allow you to do just that.
The following piece of code demonstrates this feature. This creates an additional instance (otherRepo) that you can use to retrieve a different version of the file currently checked out in your main working directory.
string repoPath = "path/to/your/repo";
// Create a temp folder for a second working directory
string tempWorkDir = Path.Combine(Path.GetTempPath(), "tmp_wd");
Directory.CreateDirectory(newWorkdir);
// Also create a new index to not alter the main repository
string tempIndex = Path.Combine(Path.GetTempPath(), "tmp_idx");
var opts = new RepositoryOptions
{
WorkingDirectoryPath = tempWorkDir,
IndexPath = tempIndex
};
using (var mainRepo = new Repository(repoPath))
using (var otherRepo = new Repository(mainRepo.Info.Path, opts))
{
string path = "file.txt";
// Do your stuff with mainrepo
mainRepo.CheckoutPaths("HEAD", new[] { path });
var currentVersion = File.ReadAllText(Path.Combine(mainRepo.Info.WorkingDirectory, path));
// Use otherRepo to temporarily checkout previous versions of files
// Thank to the passed in RepositoryOptions, this checkout will not
// alter the workdir nor the index of the main repository.
otherRepo.CheckoutPaths("HEAD~2", new [] { path });
var olderVersion = File.ReadAllText(Path.Combine(otherRepo.Info.WorkingDirectory, path));
}
You can get a better grasp of this RepositoryOptions type by taking a look at the tests in RepositoryOptionFixture that exercise it.

Categories