Accepting audio file in api - c#

I am trying to convert my API to accept an audio file instead of a string but after looking into it I can not find a suitable example on how to do so.
At the moment the Speech-To-Text service is run locally but I want to move this to the server. The API call to the wit.ai services I already made. The thing left is to make the API accept a audio file(audio will always be .wav)
If anyone has some suggestions let me know I am stuck on this
[Produces("application/json")]
[Route("api")]
public class CommandApiController : Controller
{
// constructor
[HttpPost]
public async Task<IActionResult> ProcessCommandAsync([FromBody]string command)
{
// Testing SpeechToText method
string path = #"C:\Users\rickk\Desktop\SmartSpeaker\agenda.wav";
// Overridden for now
command = await CovnvertSpeechToTextApiCall(new ByteArrayContent(System.IO.File.ReadAllBytes(path)));
// Logic
}
async Task<string> CovnvertSpeechToTextApiCall(ByteArrayContent content)
{
var httpClient = new HttpClient();
content.Headers.ContentType = MediaTypeHeaderValue.Parse("audio/wav");
// Wit.ai server token
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token);
var httpResponseMessage = await httpClient.PostAsync("https://api.wit.ai/speech", content);
if (httpResponseMessage.IsSuccessStatusCode)
{
var response = await httpResponseMessage.Content.ReadAsStringAsync();
var modeldata = Newtonsoft.Json.JsonConvert.DeserializeObject<Model.DeserializedJsonDataModel>(response);
return modeldata._text;
}
else
{
return null;
}
}
}

You can easily upload a file into Asp.net core web api using IFormFile as following where you accept a parameter of the previous type inside your post action
[HttpPost]
public async Task<IActionResult> UploadAudioFile(IFormFile file)
{
/*
* the content types of Wav are many
* audio/wave
* audio/wav
* audio/x-wav
* audio/x-pn-wav
* see "https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types"
*/
if (file.ContentType != "audio/wave")
{
return BadRequest("Wrong file type");
}
var uploads = Path.Combine(HostingEnvironment.WebRootPath, "uploads");//uploads where you want to save data inside wwwroot
var filePath = Path.Combine(uploads, file.FileName);
using (var fileStream = new FileStream(filePath, FileMode.Create))
{
await file.CopyToAsync(fileStream);
}
return Ok("File uploaded successfully");
}
You will need to ask for IHostingEnvironment object in your controller constructor using dependency injection as following:
public FileController(IHostingEnvironment hostingEnvironment)
{
HostingEnvironment = hostingEnvironment;
}
And then assign it to property inside your controller.
Beside that don't forget to send your request from the client as Multipart form as following (example):

Thanks for the comment.
Not exactly what I was looking for but that is my fault for not explaining it correctly. Here is what I have found as a solution.
[Produces("application/json")]
[Route("api/audio")]
[HttpPost]
public async Task<IActionResult> ProcessCommandAsync([FromForm]IFormFile command)
{
if(command.ContentType != "audio/wav" && command.ContentType != "audio/wave" || command.Length < 1)
{
return BadRequest();
}
var text = await CovnvertSpeechToTextApiCall(ConvertToByteArrayContent(command));
return Ok(FormulateResponse(text));
}
private ByteArrayContent ConvertToByteArrayContent(IFormFile audofile)
{
byte[] data;
using (var br = new BinaryReader(audofile.OpenReadStream()))
{
data = br.ReadBytes((int) audofile.OpenReadStream().Length);
}
return new ByteArrayContent(data);
}

Related

Sending a file with RestClient request

I have a service in .Net Core which takes an IFormFile as a parameter. I would like to send the data from this IFormFile up to an Api. The code below is the auto generated code from postman which worked for my request. In the 'attachmentRequest.AddFile("file", file);' line, file is a string of the local path which postman uploaded the file from. What is the best way to send my IFormfile as the file sent? Do I use a stream and save the IFormfile to a location in .Net Core and pass that address?
public async Task<bool> PostIssue(IFormFile file)
{
var client = new RestClient("https://sample/10000/attachments");
client.Timeout = -1;
var attachmentRequest = new RestRequest(Method.POST);
attachmentRequest.AddHeader("X-Atlassian-Token", "no-check");
attachmentRequest.AddHeader("Authorization", "Basic xxxxxxxxxxxxxx=");
attachmentRequest.AddHeader("Cookie", "atlassian.xsrf.token=xxxxxxxxxxxxxxxxxx_lin");
attachmentRequest.AddFile("file", file);
IRestResponse attachmentResponse = client.Execute(attachmentRequest);
Console.WriteLine(attachmentResponse.Content);
if (attachmentResponse.IsSuccessful)
{
return true;
}
else
{
return false;
}
}
You use the second method of AddFile:
public IRestRequest AddFile (string name, byte [] bytes, string fileName, string contentType = null);
Convert the IFormFile file to byte array format instead of getting the path to transfer the file.
What you said is that uploading to the local using stream is indeed possible, but this is an extra action, you can directly operate IFormFile into byte form and transfer it to the API, as shown below:
public async Task<bool> PostIssue(IFormFile file)
{
var client = new RestClient("http://localhost:50855/Users");
client.Timeout = -1;
var attachmentRequest = new RestRequest(Method.POST);
attachmentRequest.AddHeader("X-Atlassian-Token", "no-check");
attachmentRequest.AddHeader("Authorization", "Basic xxxxxxxxxxxxxx=");
attachmentRequest.AddHeader("Cookie", "atlassian.xsrf.token=xxxxxxxxxxxxxxxxxx_lin");
using (var ms = new MemoryStream())
{
file.CopyTo(ms);
var fileBytes = ms.ToArray();
attachmentRequest.AddFile("filename",fileBytes, file.FileName);
}
IRestResponse attachmentResponse = client.Execute(attachmentRequest);
Console.WriteLine(attachmentResponse.Content);
if (attachmentResponse.IsSuccessful)
{
return true;
}
else
{
return false;
}
}
Api:
public async Task<IActionResult> attachments()
{
var file = Request.Form.Files["filename"]; //get the file
//do something you want
return Ok();
}
Here is the test result:

How to pass on the content of a HttpInputStream and send it to another Rest Api in c# .net

I have a REST API Web Service that acts as middleware that redirects calls onto another REST API Service.
For example in this case I upload a file on the webpage. And this Web Service receives the files and sends them to another API that actually processes the files.
It is something like this:
public async Task<IHttpActionResult> ExecuteFileUpload()
{
IHttpActionResult res;
try
{
var httpRequest = HttpContext.Current.Request;
var requestedFiles = new List<System.IO.Stream>();
var url = "http://localhost:2288" + "/api/v1/templates/upload";
if (httpRequest.Files.Count > 0)
{
HttpFileCollection files = httpRequest.Files;
using (var content = new MultipartFormDataContent())
{
int index = 0;
foreach (var file in httpRequest.Files)
{
var postedFile = httpRequest.Files[index];
var fileName = postedFile.FileName;
var fileInMemory = postedFile.InputStream;
content.Add(new StreamContent(fileInMemory), "f" + index, fileName);
index++;
}
res = await ForwardPost(url, content);
}
}
else
res = BadRequest();
}
catch (Exception ex)
{
res = InternalServerError(ex);
}
return res;
}
The forward post function is simple just like this:
protected async Task<IHttpActionResult> ForwardPost(string url, MultipartFormDataContent forwardContent)
{
IHttpActionResult res;
using (var client = CreateClient())
{
using (var response = await client.PostAsync(url, forwardContent))
{
if (response.StatusCode == HttpStatusCode.OK)
{
var content = await response.Content.ReadAsAsync<JToken>();
res = Ok(content);
}
else
{
res = InternalServerError();
}
}
return res;
}
}
As you can see I just want to forward whatever is passed to me from the webpage and forward it to the actual REST API that handles this.
However it throws an exception on this line:
response = await client.PostAsync(url, forwardContent)
It throws a System.IO.IOException
Cannot close stream until all bytes are written.
Why is this the case?
Is there a way to solve this problem?
The using in the ForwardPost function will dispose the forwardedContent from the calling method. This dispose will attempt to dispose the "postedFile.InputStream" reference from the request object of the origin method. This is likely deeply tied to the httprequest object.
using (var response = await client.PostAsync(url, forwardContent))
The solution is to copy the postedFile.InputStream to a new memorystream such that it can be disposed separately.

How to submit a file and some other form content to Web API using C# HttpClient

I am trying to submit a file with some KeyValuePairs.(which is id in this case) using HttpClient in C#. the File is being submitted but i cannot read the KeyValuePairs
This is my controller.
[HttpPost]
public async Task<ActionResult> Index(HttpPostedFileBase File)
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:65211/");
MultipartFormDataContent form = new MultipartFormDataContent();
//Here I am adding a file to a form
HttpContent content = new StringContent("fileToUpload");
form.Add(content, "fileToUpload");
var stream = File.InputStream;
content = new StreamContent(stream);
content.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
{
Name = "fileToUpload",
FileName = File.FileName
};
form.Add(content);
// and here i am adding a dictionary with one keyvaluepair
Dictionary<string, string> Parameters = new Dictionary<string, string>();
Parameters.Add("id", "3");
form.Add(new FormUrlEncodedContent(Parameters));
//this will hit the api
var response = await client.PostAsync("/api/Upload", form);
var k = response.Content.ReadAsStringAsync().Result;
return View();
}
This is the Api Code
[Route("api/Upload")]
[HttpPost]
// i have tested public async Task<HttpResponseMessage> Upload(string id) <= giving parameters. the api doesnt hit if i give any
public async Task<HttpResponseMessage> Upload()
{
var request = HttpContext.Current.Request;
HttpResponseMessage result = null;
if (request.Files.Count == 0)
{
result = Request.CreateResponse(HttpStatusCode.OK, "Ok");;
}
var postedFile = request.Files[0];
return Request.CreateResponse(HttpStatusCode.OK, "Ok");
}
I am able to read the file. It gets submitted to the API. the problem is the "id" that i submitted as a keyvaluepair. I don't know how to read it. If i pass parameters to the Api. client returns the error "Not Found".
I finally was able to read both the file and the parameters I sent to the Web API. It was a simple implimentation with HttpContext.Current.Request
This is how i modified the API code.
[Route("api/Upload")]
[HttpPost]
// i have tested public async Task<HttpResponseMessage> Upload(string id) <= giving parameters. the api doesnt hit if i give any
public async Task<HttpResponseMessage> Upload()
{
var request = HttpContext.Current.Request;
var key = Request.Params["key"]; // **<- LOOK AT THIS HERE**
HttpResponseMessage result = null;
if (request.Files.Count == 0)
{
result = Request.CreateResponse(HttpStatusCode.OK, "Ok");;
}
var postedFile = request.Files[0];
return Request.CreateResponse(HttpStatusCode.OK, "Ok");
}
By using HttpContext.Current.Request.Params, I was able to read the other values from the api. Request.Files contains all the files and Request.Params contains all string parameters.

Post files from ASP.NET Core web api to another ASP.NET Core web api

We are building a web application that consist of an Angular2 frontend, a ASP.NET Core web api public backend, and a ASP.NET Core web api private backend.
Uploading files from Angular2 to the public backend works. But we would prefer to post them forward to the private backend.
Current working code
[HttpPost]
public StatusCodeResult Post(IFormFile file)
{
...
}
From there I can save the file to disk using file.CopyTo(fileStream);
However, I want to re-send that file, or those files, or, ideally, the whole request to my second web api core.
I am not sure how to achieve this with the HttpClient class of asp.net core.
I've tried all kinds of things such as
StreamContent ss = new StreamContent(HttpContext.Request.Body);
var result = client.PostAsync("api/Values", ss).Result;
But my second backend gets an empty IFormFile.
I have a feeling it is possible to send the file(s) as a stream and reconstruct them on the other side, but can't get it to work.
The solution must use two web api core.
Solution
Public backend in DMZ
[HttpPost]
public StatusCodeResult Post(IFormFile file)
{
try
{
if (file != null && file.Length > 0)
{
using (var client = new HttpClient())
{
try
{
client.BaseAddress = new Uri(currentPrivateBackendAddress);
byte[] data;
using (var br = new BinaryReader(file.OpenReadStream()))
data = br.ReadBytes((int)file.OpenReadStream().Length);
ByteArrayContent bytes = new ByteArrayContent(data);
MultipartFormDataContent multiContent = new MultipartFormDataContent();
multiContent.Add(bytes, "file", file.FileName);
var result = client.PostAsync("api/Values", multiContent).Result;
return StatusCode((int)result.StatusCode); //201 Created the request has been fulfilled, resulting in the creation of a new resource.
}
catch (Exception)
{
return StatusCode(500); // 500 is generic server error
}
}
}
return StatusCode(400); // 400 is bad request
}
catch (Exception)
{
return StatusCode(500); // 500 is generic server error
}
}
Private backend
[HttpPost]
public void Post()
{
//Stream bodyStream = HttpContext.Request.Body;
if (Request.HasFormContentType)
{
var form = Request.Form;
foreach (var formFile in form.Files)
{
var targetDirectory = Path.Combine(_appEnvironment.WebRootPath, "uploads");
var fileName = GetFileName(formFile);
var savePath = Path.Combine(targetDirectory, fileName);
using (var fileStream = new FileStream(savePath, FileMode.Create))
{
formFile.CopyTo(fileStream);
}
}
}
}
Hi i had the same issue and this is what worked for me :
My setup is netCore MVC netCoreApi.
My MVC Controller looks like :
[HttpPost("UploadFiles")]
public async Task<IActionResult> Post(List<IFormFile> files)
{
Sp4RestClient dataPovider = new Sp4RestClient("http://localhost:60077/");
long size = files.Sum(f => f.Length);
foreach (var file in files)
{
await dataPovider.ImportFile(file);
}
return Ok();
}
DataProvider Method :
public async Task ImportFile(IFormFile file)
{
RestClient restClient = new RestClient(_queryBulder.BuildImportFileRequest());
using (var content = new MultipartFormDataContent())
{
content.Add(new StreamContent(file.OpenReadStream())
{
Headers =
{
ContentLength = file.Length,
ContentType = new MediaTypeHeaderValue(file.ContentType)
}
}, "File", "FileImport");
var response = await restClient.Post<IFormFile>(content);
}
}
And least my WebApi Controller :
[HttpPost]
[Route("ImportData")]
public IActionResult Import(IFormFile file)
{
return Ok();
}
To see the complete code here is my RestClient Post method :
public async Task<RestResult<T>> Post<T>(HttpContent content)
{
using (HttpClient httpClient = new HttpClient())
{
HttpResponseMessage response = await httpClient.PostAsync(Endpoint, content);
if (response.StatusCode == HttpStatusCode.Created)
{
T result = JsonConvert.DeserializeObject<T>(await response.Content.ReadAsStringAsync());
return new RestResult<T> { Result = result, ResultCode = HttpStatusCode.OK };
}
RestResult<T> nonOkResult =
new RestResult<T> { Result = default(T), ResultCode = response.StatusCode, Message = await response.Content.ReadAsStringAsync() };
return nonOkResult;
}
}
// Yeah i know im not getting HttpStatusCode.Created back ;)
happy coding ;)
API Code
[Route("api/upload/{id}")]
[HttpPost]
public async Task<IActionResult> Post(string id)
{
var filePath = #"D:\" + id; //+ Guid.NewGuid() + ".png";
if (Request.HasFormContentType)
{
var form = Request.Form;
foreach (var formFile in form.Files)
{
if (formFile.Length > 0)
{
using (var stream = new FileStream(filePath, FileMode.Create))
{
await formFile.CopyToAsync(stream);
}
}
}
}
return Ok(new { Path = filePath });
}
Back End
[Route("home/UploadFile")]
[HttpPost]
public IActionResult UploadFile(IFormFile file)
{
if (file == null || file.Length == 0)
return Content("file not selected");
var client = new HttpClient();
byte[] data;
using (var br = new BinaryReader(file.OpenReadStream()))
data = br.ReadBytes((int)file.OpenReadStream().Length);
ByteArrayContent bytes = new ByteArrayContent(data);
MultipartFormDataContent multiContent = new MultipartFormDataContent
{
{ bytes, "file", file.FileName }
};
var result = client.PostAsync("http://localhost:2821/api/upload/" + file.FileName, multiContent).Result;
return RedirectToAction("file");
}
Download Source
I was in a similar situation - I needed a proxy method for forwarding not only files but also JSON data and whatnot. I did not want to do any analysis of the data in my proxy to let the final receiver deal with it.
So with some help from #Anton Tykhyy I came to the following working solution:
byte[] arr = null;
using (var mems = new MemoryStream())
{
// read entire body into memory first because it might be chunked with unknown length
await request.Body.CopyToAsync(mems);
await mems.FlushAsync(); // not sure if needed after CopyToAsync - better safe then sorry
arr = mems.ToArray();
}
msg.Content = new ByteArrayContent(arr);
msg.Content.Headers.ContentLength = arr.Length;
// keep content-type header "as is" to preserve multipart boundaries etc.
msg.Content.Headers.TryAddWithoutValidation("Content-Type", request.ContentType);
var response = await _httpClient.SendAsync(msg);
I tested it with complex request that contained multipart form data with JSON field and multiple attached files, and all the data reached my backend server without any issues.
Ignoring the HttpClient when you call the private backend API, can you reference the private Core API project from the public Core API project and call the controller directly from the Core API project? See the request is still null/empty. If the request comes out with a value then the issue is with the use of the HttpClient.
Ideally, you want to create a package library(kind of SDK) for your private Core API that you want to distribute to consuming clients. This acts like a wrapper/proxy. This way you can isolate the private backend system and you can troubleshoot it in isolation. So you public Core API project(which is the private backend client) can reference it as nuget package.

Download Binary from WebApi2 As File. Server Side

I've looked a couple of answers on here but still having an issue.
One system is saving a binary byte[] into SQL 2014 with Entity Framework 6.
I have a "name" of the record (not the file) but I want to serve up the binary data as a downloadable file via the webapi2. I have it somewhat working but on the browser it shows the ID as the file name and says can't download file. It is prompting to download but then it can't.
Right now for PoC I'm hard coding the mime type to word docs. What am I doing wrong and how should I refactor this to also provide a file name.
I'm saving the document using Office Apps, Task Pane:
https://msdn.microsoft.com/en-us/library/office/jj715284.aspx
FileType: "Compressed" = Returns the entire document (.pptx or .docx) in Office Open XML (OOXML) format as a byte array.
Office.context.document.getSelectedDataAsync(Office.CoercionType.Text,
function (result) {
if (result.status === Office.AsyncResultStatus.Succeeded) {
vm.data.data = result.value;
//I call a angularJs service which post..
//return $http.post('https://stapi.local:8443/api/activities', vm.data);
} else {
}
}
);
Then I try to let a user download the document with the following.
WebAPI2 Controller:
[HttpGet, Route("api/activityObjectFile/{id}/{name}")]
public HttpResponseMessage GetDataFile(int id)
{
var fileByte = _activityService.GetFile(id);
HttpResponseMessage response = new HttpResponseMessage { Content = new StreamContent(new MemoryStream(fileByte))};
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/msword");
response.Content.Headers.ContentLength = fileByte.Length;
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment filename=test.docx");
return response;
}
Class Service Library:
public Byte[] GetFile(int id)
{
var existingActivityObjectFile = _repo.QueryAll<ActivityObject>().Where(a => a.Id == id).Select(a => a.BinaryData).First();
return existingActivityObjectFile;
}
Client Url:
Download
This should get you going for now:
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StreamContent(new MemoryStream(fileByte))
};
response.Content
.Headers
.Add("Content-Type", "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "test.docx"
};
return response;
As for suggestions for refactoring, you could use my method.
I have an implementation of the IHttpActionResult that I return, it looks like this:
public class DocumentAttachmentResult : IHttpActionResult {
private readonly string fileName;
private readonly string mimeType;
private readonly byte[] blob;
public DocumentAttachmentResult(string fileName, string mimeType, byte[] blob) {
this.fileName = fileName;
this.mimeType = mimeType;
this.blob = blob;
}
private HttpResponseMessage Execute() {
var response = new HttpResponseMessage(HttpStatusCode.OK) { Content = new StreamContent(new MemoryStream(this.blob)) };
response.Content.Headers.Add("Content-Type", this.mimeType);
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = this.fileName };
return response;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken) {
return Task.FromResult(this.Execute());
}
}
And my controller action looks like this:
[HttpGet]
[Route("attachments/{id:guid}/download")]
public IHttpActionResult DownloadAttachment(Guid id) {
var attachment = this.repository.FindById(id);
if (attachment == null) {
return this.NotFound();
}
return new DocumentAttachmentResult(attachment.Name, attachment.FileType.MimeType, attachment.BinaryBlob);
}
I store the file name, mime type and binary in SQL server have it modeled as one entity called Attachment. The mime type and file is captured when I upload a file using another action on my WebApi controller.

Categories

Resources