Angular 7 send multiple files to the server - angular7

I have a question about sending a collection of File from an Angular 7 application to a .NET Core 3 server.
I have functioning code for sending a single File to the server. But I have hit a problem when I try to send a collection of File - i.e: File[]. This is the controller action:
[HttpPost]
public async Task<IActionResult> Upload([FromForm(Name = "files")] List<IFormFile> files)
{
// implementation...
{
I send FormData in line with code I have used to send a single file to the server:
const formData = new FormData();
formData.append('files', this.files);
which causes an error with the compiler (see screenshot):
The FormData cannot deal with the collection of File[].
If I were to change the same code to handle one single File, for example by choosing the first element in the collection, the File is successfully sent to the server. Like so:
const formData = new FormData();
formData.append('files', this.files[0]);
The request is sent like this:
const httpOptions = {
headers: new HttpHeaders({ 'Content-Disposition' : 'multipart/form-data' }),
};
postPhotos(model: FormData): Observable<any | ErrorReportViewModel> {
return this.http.post('api/Photograph', model, httpOptions)
.pipe(
catchError(error => this.httpErrorHandlerService.handleHttpError(error)));
}
I have also attempted to send the files in the request body with a model. This does not work: the server receives a Null collection of IFormFile. My research suggests that sending FormData is the way to go. But the FormData seems not to accept the collection of Files.
How can I send the collection of Files to the server?

You just need to call append method multiple times:
const formData = new FormData();
this.files.forEach((file) => { formData.append('files[]', file); });
Wether the key has to be 'files[]' or can just be 'files' depends on the serverside platform that parses the body.

Related

How to upload a file to 3rd API service using ASP.NET MVC 5 app?

I have an asp.net mvc 5 based app. One of my methods allow the user to upload file via an html-form.
When the user post the file the the framework binds binds the form-data to a view-model called UploadFileViewModel. The UploadFileViewModel has a property called UploadedFile with the type HttpPostedFileBase which gives me the info about the upload file. Typically, I would use the SaveAs() method on the HttpPostedFileBase class to save the temp file into a permanent place on the server.
However, in this case, I don't want to save the file to the server, instead I need to call a 3rd party API to store the file. The API, expects 3 parameters
file (type file)
title (type string)
recordType (type string)
How can I use the WebClient class to upload the HttpPostedFileBase object along with the title, and recordType parameters to the API?
The WebClient class has a method called UploadFile which accepts uri (string) and a fileName (string) How can I use this method to make the call to the remote API?
Try with the HttpClient class
public async Task UploadAsync()
{
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri("http://hello.net/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
MultipartFormDataContent content = new MultipartFormDataContent();
ByteArrayContent fileContent = new ByteArrayContent(System.IO.File.ReadAllBytes("C:/Images/testing.png"));
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = "testing.png" };
content.Add(fileContent);
HttpResponseMessage response = await client.PostAsync("api/SaveFile?title=hello&recordType=audio", content);
.....
....
}
}

Response Header issue on Azure Web Application

I am not sure what is happening here.
When I run my web application locally and click a button to download a file, the file is downloaded fine and Response header as you can see in the attached screenshot where it says local.
But when I publish the application to azure web app. Somehow the download button stops working. I checked the Response Header and you can see the difference.
What would cause this problem? The code is the same? Is there any settings that I should be setting in azure web app in azure portal?
Updated to add code
I have debugged remotely to figure out what is going on as #Amor suggested.
It is so strange that When I debug on my local machine first ExportTo action gets hit which prepares the TempData then Download action gets called once the first action completed with ajax call.
However, this is not the case when I debug remotely. Somehow the ExportTo action never gets called. It directly calls the Download action. As a result the TempData null checking is always null.
But why? Why on earth and how that is possible? Is there something cached somewhere?
I have wiped the content of web application on the remote and re-publish evertyhing to ensure everything is updated. But still no success.
here is the code:
[HttpPost]
public virtual ActionResult ExportTo(SearchVm searchVm)
{
var data = _companyService.GetCompanieBySearchTerm(searchVm).Take(150).ToList();
string handle = Guid.NewGuid().ToString();
TempData[handle] = data;
var fileName = $"C-{handle}.xlsx";
var locationUrl = Url.Action("Download", new { fileGuid = handle, fileName });
var downloadUrl = Url.Action("Download");
return Json(new { success = true, locationUrl, guid = handle, downloadUrl }, JsonRequestBehavior.AllowGet);
}
[HttpGet]
public ActionResult Download(string fileGuid, string fileName)
{
if (TempData[fileGuid] != null)
{
var fileNameSafe = $"C-{fileGuid}.xlsx";
var data = TempData[fileGuid] as List<Company>;
using (MemoryStream ms = new MemoryStream())
{
GridViewExtension.WriteXlsx(GetGridSettings(fileNameSafe), data, ms);
MVCxSpreadsheet mySpreadsheet = new MVCxSpreadsheet();
ms.Position = 0;
mySpreadsheet.Open("myDoc", DocumentFormat.Xlsx, () =>
{
return ms;
});
mySpreadsheet.Document.Worksheets.Insert(0);
var image = Server.MapPath("~/images/logo.png");
var worksheet = mySpreadsheet.Document.Worksheets[0];
worksheet.Name = "Logo";
worksheet.Pictures.AddPicture(image, worksheet.Cells[0, 0]);
byte[] result = mySpreadsheet.SaveCopy(DocumentFormat.Xlsx);
DocumentManager.CloseDocument("myDoc");
Response.Clear();
//Response.AppendHeader("Set-Cookie", "fileDownload=true; path=/");
Response.ContentType = "application/force-download";
Response.AddHeader("content-disposition", $"attachment; filename={fileNameSafe}");
Response.BinaryWrite(result);
Response.End();
}
}
return new EmptyResult();
}
here is the javascript:
var exportData = function (urlExport) {
console.log('Export to link in searchController: ' + urlExport);
ExportButton.SetEnabled(false);
var objData = new Object();
var filterData = companyFilterData(objData);
console.log(filterData);
$.post(urlExport, filterData)
.done(function (data) {
console.log(data.locationUrl);
window.location.href = data.locationUrl;
});
};
When Export button is clicked exportData function is called:
var exportToLink = '#Url.Action("ExportTo")';
console.log('Export to link in index: '+exportToLink);
SearchController.exportData(exportToLink);
As I mentioned that this code works perfectly on the local machine. something weird is happening on azure webapp that ExportTo action breakpoint is never gets hit.
I am not sure what else I could change to get the ExportTo action hit?
Based on the Response Header of Azure Web App, we find that the value of Content-Length is 0. It means that no data has been sent from web app server side.
In ASP.NET MVC, we can response file using following ways.
The first way, send the file which hosted on server. For this way, please check whether the excel file has been uploaded to Azure Web App. You could use Kudu or FTP to the folder to check whether the file is exist.
string fileLocation = Server.MapPath("~/Content/myfile.xlsx");
string contentType = System.Net.Mime.MediaTypeNames.Application.Octet;
string fileName = "file.xlsx";
return File(fileLocation, contentType, fileName);
The second way, we can read the file from any location(database, server or azure storage) and send the file content to client side. For this way, please check whether the file has been read successfully. You can remote debug your azure web app to check whether the file content hasn't been read in the right way.
byte[] fileContent = GetFileContent();
string contentType = System.Net.Mime.MediaTypeNames.Application.Octet;
string fileName = "file.xlsx";
return File(fileContent, contentType, fileName);
5/27/2017 Update
Somehow the ExportTo action never gets called. It directly calls the Download action. As a result the TempData null checking is always null.
How many instances does your Web App assigned? If your Web App have multi instances, the ExportTo request is handled by one instance and the Download request is handled by another instance. Since the TempData is store in memory of dedicated instance, it can't be got from another instance. According to the remote debug document. I find out the reason why the ExportTo action never gets called.
If you do have multiple web server instances, when you attach to the debugger you'll get a random instance, and you have no way to ensure that subsequent browser requests will go to that instance.
To solve this issue, I suggest you response the data directly from the ExportTo action or save the temp data in Azure blob storage which can't be accessed from multi instances.

Returning result for .net Web Api from db call

I'm new to the .net Web API and am trying to figure out how I return a Get result from a call to my database. I know everything works in my regular MVC page. But Not sure how to return the result from the Web API controller. I thought it was as simple as returning Json with the result. Here is my code:
// GET api/<controller>
public IEnumerable<string> Get()
{
using (var _db = new JobsDatabaseEntities())
{
var user = Env.CurrentUser;
var posts =
_db.JobPostings.Where(
j =>
j.City.Equals(user.City, StringComparison.OrdinalIgnoreCase) &&
j.Industry.ID == user.Industry.ID);
var result = new List<BusJobPost>();
foreach (var post in posts)
{
var p = new BusJobPost(post);
result.Add(p);
}
return Json(result);
}
}
Please visit this resource: Action Results in Web API 2. Your case is described in fourth section Some other type (which is applicable to first version of Web API as well).
In Web API you don't return JSON result explicitly. It is actually done by process called Content Negotiation. You can read about it here [Content Negotiation in ASP.NET Web API] in detail.
Highlighting briefly just some of this:
You can set Accept header for you request as: Accept: application/json (for example, if you use jquery ajax function: dataType: 'json')
Or even if you don't use Accept header at all, if you send request with JSON data, you should also get response back in JSON format
So you should just return result variable from your controller action and satisfy some of conditions to get response serialized into JSON.

How to send data from client to server using Rikulo stream

I'm trying to use Rikulo stream, and i have some trouble when i want to send data from client to server.
Suppose that i have a registration form and i want send a request to check if that username already exist in my database.
I have adopted MVC pattern, so i want that the controller received data and then, using a dao class, check if username exist or not.
In client side i have this lines of code
InputElement username = query('#username');
document.query("#submit").onClick.listen((e) {
HttpRequest request = new HttpRequest();
var url = "/check-existing-username";
request.open("POST", url, async:true);
request.setRequestHeader("Content-Type", "application/json");
request.send(stringify({"user": username.value}));
});
Is this the correct way to send data?
Here my server side code
void main(){
Controller controller = new Controller();
var _mapping = {
"/": controller.home,
"/home": controller.home,
"/check-existing-username" : controller.checkUsername
};
new StreamServer(uriMapping: _mapping).start();
And my controller method
void checkUsername(HttpConnect connect) {
//How to access data received from client?
}
The dao class is already defined, so i want only know how to access data.
I hope that someone can help me.
Since you're using POST, the JSON data will be in the HTTP request's body. You can retrieve it there. Rikulo Commons has a utility called readAsJson. You can utilize it as follows.
import "package:rikulo_commons/convert.dart";
Future checkUsername(HttpConnect connect) {
return readAsJson(connect.request).then((Map<String, String> data) {
String username = data["user"];
//...doa...
});
}
Notice that reading request's body is asynchronous, so you have to return a Future instance to indicate when it is done.

Getting 401 Unauthorized from Google API Resumable Update

I am trying to integrate upload of arbitrary files to Google Docs into an existing application. This used to work before using resumable upload became mandatory. I am using Java client libraries.
The application is doing the upload in 2 steps:
- get the resourceId of the file
- upload the data
To get the resourceId I am uploading a 0-size file (i.e. Content-Length=0). I am passing ?convert=false in the resumable URL (i.e. https://docs.google.com/feeds/upload/create-session/default/private/full?convert=false).
I am passing "application/octet-stream" as content-type. This seems to work, though I do get different resourcesIds - "file:..." resourceIds for things like images, but "pdf:...." resourceIds for PDFs.
The second step constructs a URL based on the resourceId obtained previously and performs a search (getEntry). The URL is in the form of https://docs.google.com/feeds/default/private/full/file%3A.....
Once the entry is found the ResumableGDataFileUploader is used to update the content (0-byte file) with the actual data from the file being uploaded. This operation fails with 401 Unauthorized response when building ResumableGDataFileUploader instance.
I've tried with ?convert=false as well as ?new-revision=true and both of these at the same time. The result is the same.
The relevant piece of code:
MediaFileSource mediaFile = new MediaFileSource(
tempFile, "application/octet-stream");
final ResumableGDataFileUploader.Builder builder =
new ResumableGDataFileUploader.Builder(client, mediaFile, documentListEntry);
builder.executor(MoreExecutors.sameThreadExecutor());
builder.requestType(ResumableGDataFileUploader.RequestType.UPDATE);
// This is where it fails
final ResumableGDataFileUploader resumableGDataFileUploader = builder.build();
resumableGDataFileUploader.start();
return tempFile.length();
The "client" is an instance of DocsService, configured to use OAuth. It is used to find "documentListEntry" immediately before the given piece of code.
I had to explicitly specify request type, since it seems the client library code contains a bug causing NullPointerException for "update existing entry" case.
I have a suspicion that the issue is specifically in the sequence of actions (upload 0-byte file to get the resourceId, then update with actual file) but I can't figure out why it doesn't work.
Please help?
This code snippet works for me using OAuth 1.0 and OAuth 2.0:
static void uploadDocument(DocsService client) throws IOException, ServiceException,
InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(10);
File file = new File("<PATH/TO/FILE>");
String mimeType = DocumentListEntry.MediaType.fromFileName(file.getName()).getMimeType();
DocumentListEntry documentEntry = new DocumentListEntry();
documentEntry.setTitle(new PlainTextConstruct("<DOCUMENT TITLE>"));
int DEFAULT_CHUNK_SIZE = 2 * 512 * 1024;
ResumableGDataFileUploader.Builder builder =
new ResumableGDataFileUploader.Builder(
client,
new URL(
"https://docs.google.com/feeds/upload/create-session/default/private/full?convert=false"),
new MediaFileSource(file, mimeType), documentEntry).title(file.getName())
.requestType(RequestType.INSERT).chunkSize(DEFAULT_CHUNK_SIZE).executor(executor);
ResumableGDataFileUploader uploader = builder.build();
Future<ResponseMessage> msg = uploader.start();
while (!uploader.isDone()) {
try {
Thread.sleep(100);
} catch (InterruptedException ie) {
throw ie; // rethrow
}
}
DocumentListEntry uploadedEntry = uploader.getResponse(DocumentListEntry.class);
// Print the document's ID.
System.out.println(uploadedEntry.getId());
System.out.println("Upload is done!");
}

Resources