Retrofit: Uploading file image within a model - retrofit

Given I have these:
public Class Attachment{
public String id;
public File imageFile;
}
// Service
#Multipart
#POST("attachments")
Call<Void> upload(#Part("attachment") Attachment attachment);
How can I successfully upload the image file? I know there is the other technique of using #Part MultipartBody.Part file but I want a better approach so I can submit the data in this format:
attachment: {
id: 1
imageFile: //image data here
}

I also tried to upload file using java Model class but unfortunately retrofit doesn't support that.
I guess you can only send RequestBody file alone not with other values.
More over if passing id is necessary then you can pass it in header.
Here is how my file upload request looks like
postUserImage(#Header("accept-version") String api_version ,#Header("file_id") String file_id, #Part("image\"; filename=\"image.jpg ") RequestBody image);

Related

How to use "Azure storage blobs" for POST method in controller

I am creating an app where user can upload their text file and find out about its most used word.
I have tried to follow this doc to get used to the idea of using AZURE STORAGE BLOBS - https://learn.microsoft.com/en-us/azure/storage/blobs/storage-quickstart-blobs-dotnet
But I am super newbie and having a hard time figuring it out how to adapt those blobs methods for my POST method.
This my sudo - what I think I need in my controller and what needs to happen when POST method is triggered.
a.No need for DELETE or PUT, not replacing the data nor deleting in this app
b.Maybe need a GET method, but as soon as POST method is triggered, it should pass the text context to the FE component
POST method
connect with azure storage account
if it is a first time of POST, create a container to store the text file
a. how can I connect with the existing container if the new container has already been made? I found this, but this is for the old CloudBlobContainer. Not the new SDK 12 version.
.GetContainerReference($"{containerName}");
upload the text file to the container
get the chosen file's text content and return
And here is my controller.
public class HomeController : Controller
{
private IConfiguration _configuration;
public HomeController(IConfiguration Configuration)
{
_configuration = Configuration;
}
public IActionResult Index()
{
return View();
}
[HttpPost("UploadText")]
public async Task<IActionResult> Post(List<IFormFile> files)
{
if (files != null)
{
try
{
string connectionString = Environment.GetEnvironmentVariable("AZURE_STORAGE_CONNECTION_STRING");
BlobServiceClient blobServiceClient = new BlobServiceClient(connectionString);
string containerName = "textdata" + Guid.NewGuid().ToString();
BlobContainerClient containerClient = await blobServiceClient.CreateBlobContainerAsync(containerName);
//Q. How to write a if condition here so if the POST method has already triggered and container already created, just upload the data. Do not create a new container?
string fileName = //Q. how to get the chosen file name and replace with newly assignmed name?
string localFilePath = //Q. how to get the local file path so I can pass on to the FileStream?
BlobClient blobClient = containerClient.GetBlobClient(fileName);
using FileStream uploadFileStream = System.IO.File.OpenRead(localFilePath);
await blobClient.UploadAsync(uploadFileStream, true);
uploadFileStream.Close();
string data = System.IO.File.ReadAllText(localFilePath, Encoding.UTF8);
//Q. If I use fetch('Home').then... from FE component, will it receive this data? in which form will it receive? JSON?
return Content(data);
}
catch
{
//Q. how to use storageExeption for the error messages
}
finally
{
//Q. what is suitable to execute in finally? return the Content(data) here?
if (files != null)
{
//files.Close();
}
}
}
//Q. what to pass on inside of the Ok() in this scenario?
return Ok();
}
}
Q1. How can I check if the POST method has been already triggered, and created the Container? If so how can I get the container name and connect to it?
Q2. Should I give a new assigned name to the chosen file? How can I do so?
Q3. How can I get the chosen file's name so I can pass in order to process Q2?
Q4. How to get the local file path so I can pass on to the FileStream?
Q5. How to return the Content data and pass to the FE? by using fetch('Home').then... like this?
Q6. How can I use storageExeption for the error messages
Q7. What is suitable to execute in finally? return the Content(data) here?
Q8. What to pass on inside of the Ok() in this scenario?
Any help is welcomed! I know I asked a lot of Qs here. Thanks a lot!
Update: add a sample code, you can modify it as per your need.
[HttpPost]
public async Task<IActionResult> SaveFile(List<IFormFile> files)
{
if (files == null || files.Count == 0) return Content("file not selected");
string connectionString = "xxxxxxxx";
BlobServiceClient blobServiceClient = new BlobServiceClient(connectionString);
string containerName = "textdata" + Guid.NewGuid().ToString();;
BlobContainerClient containerClient = blobServiceClient.GetBlobContainerClient(containerName);
containerClient.CreateIfNotExists();
foreach (var file in files)
{
//use this line of code to get file name
string fileName = Path.GetFileName(file.FileName);
BlobClient blobClient = containerClient.GetBlobClient(fileName);
//directly read file content
using (var stream = file.OpenReadStream())
{
await blobClient.UploadAsync(stream);
}
}
//other code
return View();
}
Original answer:
When using List<IFormFile>, you should use foreach code block to iterate each file in the list.
Q2. Should I give a new assigned name to the chosen file? How can I do
so?
If you want to keep the file original name, in the foreach statement like below:
foreach (var file in myfiles)
{
Path.GetFileName(file.FileName)
//other code
}
And if you want to assign a new file name when uploaded to blob storage, you should define the new name in this line of code: BlobClient blobClient = containerClient.GetBlobClient("the new file name").
Q3. How can I get the chosen file's name so I can pass in order to
process Q2?
refer to Q2.
Q4. How to get the local file path so I can pass on to the FileStream?
You can use code like this: string localFilePath = file.FileName; to get the path, and then combine with the file name. But there is a better way, you can directly use this line of code Stream uploadFileStream = file.OpenReadStream().
Q5. How to return the Content data and pass to the FE? by using
fetch('Home').then... like this?
Not clear what's it meaning. Can you provide more details?
Q6. How can I use storageExeption for the error messages
The storageExeption does not exist in the latest version, you should install the older one.
You can refer to this link for more details.
#Ivan's answer is what the documentation seems the recommend; however, I was having a strange issue where my stream was always prematurely closed before the upload had time to complete. To anyone else who might run into this problem, going the BinaryData route helped me. Here's what that looks like:
await using var ms = new MemoryStream();
await file.CopyToAsync(ms);
var data = new BinaryData(ms.ToArray());
await blobClient.UploadAsync(data);

Can I disable model binding and use the raw request body in an action in dotnet core?

I want to setup an endpoint for testing webhooks from third parties. Their documentation is uniformly poor and there is no way ahead of time to tell exactly what I will be getting. What I've done is setup an ApiController that will just take a request and add a row to a table with what they are sending. This lets me at least verify they are calling the webhook, and to see the data so I can program to it.
// ANY api/webook/*
[Route("{*path}")]
public ActionResult Any(string path)
{
string method = Request.Method;
string name = "path";
string apiUrl = Request.Path;
string apiQuery = Request.QueryString.ToString();
string apiHeaders = JsonConvert.SerializeObject(Request.Headers);
string apiBody = null;
using (StreamReader reader = new StreamReader(Request.Body))
{
apiBody = reader.ReadToEnd();
}
Add(method, name, apiUrl, apiQuery, apiHeaders, apiBody);
return new JsonResult(new { }, JsonSettings.Default);
}
This works great, except for this new webhook I am usign that posts as form data so some middleware is reading the body and it ends up null in my code. Is there any way to disable the model processing so I can get at the request body?
You could actually use model binding to your advantage and skip all that stream reading, using the FromBody attribute. Try this:
[Route("{*path}")]
[HttpPost]
public ActionResult Any(string path, [FromBody] string apiBody)

Streaming video file in ASP.NET 5

I have problem with streaming video file in my controller to eg. VLC or HTML5 video
My code
public IActionResult VideoStreamContent()
{
var path = #"C:\video1.mp4";
var stream = System.IO.File.OpenRead(path);
return new FileStreamResult(stream, "application/octet-stream");
}
Browser can download that file from this controller but when i want make it in VLC then VLC cannot open that stream
You should try getting the proper mime type for your file before sending it down. That way clients can know how to handle the file type.
public IActionResult VideoStreamContent() {
var path = #"C:\video1.mp4";
var stream = System.IO.File.OpenRead(path);
var mimeType = GetMimeType(path);
return new FileStreamResult(stream, mimeType??"application/octet-stream");
}
string GetMimeType(string fileName) {
//Insert code here to get mime type of file
}
There are many questions/answers on SO about how to get the mime types if you look for it.

Allowing the user downloading a file located in a specific IIS folder

I have the following issue: ASP-MVC
I want to put a file in a folder in IIS and allow users surfing my site to download it.
In my site, I will have a link that points to an action method in my controller, and within this method I want to put the needed code. Never dealt with this issue before, will appriciate a code sample. Thanks!
This code , taken from this question, will accomplish what you want.
public FileResult Download()
{
byte[] fileBytes = System.IO.File.ReadAllBytes("c:\folder\myfile.ext");
string fileName = "myfile.ext";
return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
}
Assuming you want to get a specific file based on some passed-in ID, you can use the Controller.File function as described here: http://msdn.microsoft.com/en-us/library/dd492492(v=vs.100).aspx
Here's an example controller function from that page:
public ActionResult ShowFileFN(string id) {
string mp = Server.MapPath("~/Content/" + id);
return File(mp, "text/html");
}
This will return a binary stream of the named file with the specified MIME content type, in this case "text/html". You'll need to know the MIME type for each file you're returning.
Here's a function to get the MIME type of a file based on its extension:
public static string GetMimeType(string fileName)
{
string mimeType = "application/unknown";
string ext = System.IO.Path.GetExtension(fileName).ToLower();
Microsoft.Win32.RegistryKey regKey = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(ext);
if (regKey != null && regKey.GetValue("Content Type") != null)
mimeType = regKey.GetValue("Content Type").ToString();
return mimeType;
}

how to read multi part form data in .net web api controller

public class Sampleontroller:apicontroller
{
public void PostBodyMethod() {
HttpRequestMessage request=this.request;
//How to read the multi part data in the method
}
}
I am sending a multi part data to webapi controller.
How to read the contents in the method?
An 'async' example:
public async Task<HttpResponseMessage> PostSurveys()
{
// Verify that this is an HTML Form file upload request
if (!Request.Content.IsMimeMultipartContent("form-data"))
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
//Destination folder
string uploadFolder = "mydestinationfolder";
// Create a stream provider for setting up output streams that saves the output under -uploadFolder-
// If you want full control over how the stream is saved then derive from MultipartFormDataStreamProvider and override what you need.
MultipartFormDataStreamProvider streamProvider = new MultipartFormDataStreamProvider(uploadFolder );
MultipartFileStreamProvider multipartFileStreamProvider = await Request.Content.ReadAsMultipartAsync(streamProvider);
// Get the file names.
foreach (MultipartFileData file in streamProvider.FileData)
{
//Do something awesome with the files..
}
}
Have a look at the article by Mike Wasson:
http://www.asp.net/web-api/overview/working-with-http/sending-html-form-data,-part-2
Or if you are doing file uploads, here: www.strathweb.com/2012/08/a-guide-to-asynchronous-file-uploads-in-asp-net-web-api-rtm/

Resources