Trying to upload a file to onedrive that does not already exist. I have managed to get it to update an existing file. But can't seem to figure out how to create a brand new file. I have done this useing the Microsoft.Graph library.
Here is the code that works to update an existing file:
public async Task<ActionResult> OneDriveUpload()
{
string token = await GetAccessToken();
if (string.IsNullOrEmpty(token))
{
// If there's no token in the session, redirect to Home
return Redirect("/");
}
GraphServiceClient client = new GraphServiceClient(
new DelegateAuthenticationProvider(
(requestMessage) =>
{
requestMessage.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", token);
return Task.FromResult(0);
}));
try
{
string path = #"C:/Users/user/Desktop/testUpload.xlsx";
byte[] data = System.IO.File.ReadAllBytes(path);
Stream stream = new MemoryStream(data);
// Line that updates the existing file
await client.Me.Drive.Items["55BBAC51A4E4017D!104"].Content.Request().PutAsync<DriveItem>(stream);
return View("Index");
}
catch (ServiceException ex)
{
return RedirectToAction("Error", "Home", new { message = "ERROR retrieving messages", debug = ex.Message });
}
}
I'd suggest using the ChunkedUploadProvider utility that is included in the SDK. Aside from being a little easier to work with, it will allow you to upload files of any side rather than being limited to files under 4MB.
You can find a sample of how to use ChunkedUploadProvider in the OneDriveUploadLargeFile unit test.
To answer your direct question, uploading works the same for both replacing and creating files. You do however need to specify the file name rather than just an existing Item number:
await graphClient.Me
.Drive
.Root
.ItemWithPath("fileName")
.Content
.Request()
.PutAsync<DriveItem>(stream);
This code will help you all to upload small and large files using Microsoft graph Api Sdk in ASP .NEt Core
Upload or replace the contents of a DriveItem
*Controller code : -*
[BindProperty]
public IFormFile UploadedFile { get; set; }
public IDriveItemChildrenCollectionPage Files { get; private set; }
public FilesController(ILogger<FilesModel> logger, GraphFilesClient graphFilesClient, GraphServiceClient graphServiceClient, ITokenAcquisition tokenAcquisition)
{
_graphFilesClient = graphFilesClient;
_logger = logger;
_graphServiceClient = graphServiceClient;
_tokenAcquisition = tokenAcquisition;
}
[EnableCors]
[HttpPost]
[Route("upload-file")]
[RequestFormLimits(MultipartBodyLengthLimit = 100000000)]
[RequestSizeLimit(100000000)]
public async Task<IActionResult> uploadFiles(string itemId, string folderName, [FromHeader] string accessToken)
{
_logger.LogInformation("into controller");
if (UploadedFile == null || UploadedFile.Length == 0)
{
return BadRequest();
}
_logger.LogInformation($"Uploading {UploadedFile.FileName}.");
var filePath = Path.Combine(System.IO.Directory.GetCurrentDirectory(), UploadedFile.FileName);
_logger.LogInformation($"Uploaded file {filePath}");
using (var stream = new MemoryStream())
{
UploadedFile.CopyTo(stream);
var bytes = stream.ToArray();
_logger.LogInformation($"Stream {stream}.");
stream.Flush();
await _graphFilesClient.UploadFile(
UploadedFile.FileName, new MemoryStream(bytes), itemId, folderName, accessToken);
}
return Ok("Upload Successful!");
}
*Service code :-*
[EnableCors]
public async Task UploadFile(string fileName, Stream stream,string itemId,string folderName,string accessToken)
{
GraphClients graphClients = new GraphClients(accessToken);
GraphServiceClient _graphServiceClient = graphClients.getGraphClient();
_logger.LogInformation("Into Service");
var filePath = Path.Combine(System.IO.Directory.GetCurrentDirectory(),fileName);
_logger.LogInformation($"filepath : {filePath}");
Console.WriteLine("Uploading file: " + fileName);
var size = stream.Length / 1000;
_logger.LogInformation($"Stream size: {size} KB");
if (size/1000 > 4)
{
// Allows slices of a large file to be uploaded
// Optional but supports progress and resume capabilities if needed
await UploadLargeFile(filePath, stream,accessToken);
}
else
{
try
{
_logger.LogInformation("Try block");
String test = folderName + "/" + fileName;
// Uploads entire file all at once. No support for reporting progress.
// for getting your sharepoint site open graph explorer > sharepoint sites > get my organization's default sharepoint site.
var driveItem = await _graphServiceClient
.Sites["Your share point site"]
.Drive
.Root.ItemWithPath(test)
.Content
.Request()
.PutAsync<DriveItem>(stream);
_logger.LogInformation($"Upload complete: {driveItem.Name}");
}
catch (ServiceException ex)
{
_logger.LogError($"Error uploading: {ex.ToString()}");
throw;
}
}
}
private async Task UploadLargeFile(string itemPath, Stream stream,string accessToken)
{
GraphClients graphClients = new GraphClients(accessToken);
GraphServiceClient _graphServiceClient = graphClients.getGraphClient();
// Allows "slices" of a file to be uploaded.
// This technique provides a way to capture the progress of the upload
// and makes it possible to resume an upload using fileUploadTask.ResumeAsync(progress);
// Based on https://docs.microsoft.com/en-us/graph/sdks/large-file-upload
// Use uploadable properties to specify the conflict behavior (replace in this case).
var uploadProps = new DriveItemUploadableProperties
{
ODataType = null,
AdditionalData = new Dictionary<string, object>
{
{ "#microsoft.graph.conflictBehavior", "replace" }
}
};
// Create the upload session
var uploadSession = await _graphServiceClient.Me.Drive.Root
.ItemWithPath(itemPath)
.CreateUploadSession(uploadProps)
.Request()
.PostAsync();
// Max slice size must be a multiple of 320 KiB
int maxSliceSize = 320 * 1024;
var fileUploadTask = new LargeFileUploadTask<DriveItem>(uploadSession, stream, maxSliceSize);
// Create a callback that is invoked after
// each slice is uploaded
IProgress<long> progress = new Progress<long>(prog =>
{
_logger.LogInformation($"Uploaded {prog} bytes of {stream.Length} bytes");
});
try
{
// Upload the file
var uploadResult = await fileUploadTask.UploadAsync(progress);
if (uploadResult.UploadSucceeded)
{
_logger.LogInformation($"Upload complete, item ID: {uploadResult.ItemResponse.Id}");
}
else
{
_logger.LogInformation("Upload failed");
}
}
catch (ServiceException ex)
{
_logger.LogError($"Error uploading: {ex.ToString()}");
throw;
}
}
Related
I'm trying to add a new function where user can upload images and videos similar to facebook in blazor server .NET 6, I created a new api and followed microsoft documentation of uploading files, Everything is working so far with uploading up to 4 files, but when I'm trying to upload more than 4 it doesn't send a request to the api at all. and after a while it shows these two exceptionsenter image description here enter image description here
Here's the code I'm having: `
private List<File> files = new();
private List<UploadInfo> uploadResults = new();
private int maxAllowedFiles = 10;
private bool shouldRender;
protected override bool ShouldRender() => shouldRender;
private async Task OnInputFileChange(InputFileChangeEventArgs e)
{
shouldRender = false;
long maxFileSize = (long)(4 * Math.Pow(10, 8));
var upload = false;
using var content = new MultipartFormDataContent();
foreach (var file in e.GetMultipleFiles(maxAllowedFiles))
{
if (uploadResults.SingleOrDefault(
f => f.FileName == file.Name) is null)
{
try
{
var fileContent =
new StreamContent(file.OpenReadStream(maxFileSize));
fileContent.Headers.ContentType =
new MediaTypeHeaderValue(file.ContentType);
files.Add(new() { Name = file.Name });
content.Add(
content: fileContent,
name: "\"files\"",
fileName: file.Name);
upload = true;
}
catch (Exception ex)
{
Logger.LogInformation(
"{FileName} not uploaded (Err: 6): {Message}",
file.Name, ex.Message);
uploadResults.Add(
new()
{
FileName = file.Name,
ErrorCode = 6,
Uploaded = false
});
}
}
}
if (upload)
{
var client = ClientFactory.CreateClient();
var response =
await client.PostAsync($"https://localhost:7134/api/Files/FileUpload", content);
if (response.IsSuccessStatusCode)
{
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
};
using var responseStream =
await response.Content.ReadAsStreamAsync();
var newUploadResults = await JsonSerializer
.DeserializeAsync<IList<MediaAPI.UploadInfo>>(responseStream, options);
if (newUploadResults is not null)
{
uploadResults = uploadResults.Concat(newUploadResults).ToList();
}
}
}
shouldRender = true;
}`
When I try to debug it, the debug point disappears when it sends the request:
await client.PostAsync($"https://localhost:7134/api/Files/FileUpload", content);
Anyone has experiansed this issue before?
Thanks in advance.
I get an error 400 Bad Request when I try to upload a file to my OneDrive with a daemon app, using the Microsoft Graph API. I use a HttpClient, not a GraphServiceClient as the latter assumes interaction and works with a DelegatedAuthenticationProvider(?).
The App is registered in AAD and has the right Application Permission (Microsoft Graph / File ReadWrite.All)
The registration is for One Tenant and has no redirect url (as per instructions)
The main Method Upload gets an AccessToken through a Helper AuthenticationConfig and puts a file to OneDrive/SharePoint with the Helper ProtectedApiCallHelper.
[HttpPost]
public async Task<IActionResult> Upload(IFormFile file)
{
var toegang = new AuthenticationConfig();
var token = toegang.GetAccessTokenAsync().GetAwaiter().GetResult();
var httpClient = new HttpClient();
string bestandsnaam = file.FileName;
var serviceEndPoint = "https://graph.microsoft.com/v1.0/drive/items/{Id_Of_Specific_Folder}/";
var wurl = serviceEndPoint + bestandsnaam + "/content";
// The variable wurl looks as follows: "https://graph.microsoft.com/v1.0/drive/items/{Id_Of_Specific_Folder}/proefdocument.txt/content"
var apicaller = new ProtectedApiCallHelper(httpClient);
apicaller.PostWebApi(wurl, token.AccessToken, file).GetAwaiter();
return View();
}
I get a proper Access Token using the following standard helper AuthenticationConfig.GetAccessToken()
public async Task<AuthenticationResult> GetAccessTokenAsync()
{
AuthenticationConfig config = AuthenticationConfig.ReadFromJsonFile("appsettings.json");
IConfidentialClientApplication app;
app = ConfidentialClientApplicationBuilder.Create(config.ClientId)
.WithClientSecret(config.ClientSecret)
.WithAuthority(new Uri(config.Authority))
.Build();
string[] scopes = new string[] { "https://graph.microsoft.com/.default" };
AuthenticationResult result = null;
try
{
result = await app.AcquireTokenForClient(scopes).ExecuteAsync();
return result;
}
catch (MsalServiceException ex) when (ex.Message.Contains("AADSTS70011"))
{
...
return result;
}
}
With the AccessToken, the Graph-Url and the File to be uploaded (as an IFormFile) the Helper ProtectedApiCallHelper.PostWebApi is called
public async Task PostWebApi(string webApiUrl, string accessToken, IFormFile fileToUpload)
{
Stream stream = fileToUpload.OpenReadStream();
var x = stream.Length;
HttpContent content = new StreamContent(stream);
if (!string.IsNullOrEmpty(accessToken))
{
var defaultRequestHeaders = HttpClient.DefaultRequestHeaders;
HttpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/octet-stream"));
defaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", accessToken);
// Here the 400 Bad Request happens
HttpResponseMessage response = await HttpClient.PutAsync(webApiUrl, content);
if (response.IsSuccessStatusCode)
{
return;
}
else
{
//error handling
return;
}
}
}
EDIT
Please see the working solution below.
You can use GraphServiceClient without user interaction using a client id and a client secret. First, create a class called GraphAuthProvider:
public class GraphAuthProvider
{
public async Task<GraphServiceClient> AuthenticateViaAppIdAndSecret(
string tenantId,
string clientId,
string clientSecret)
{
var scopes = new string[] { "https://graph.microsoft.com/.default" };
// Configure the MSAL client as a confidential client
var confidentialClient = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithAuthority($"https://login.microsoftonline.com/{tenantId}/v2.0")
.WithClientSecret(clientSecret)
.Build();
// Build the Microsoft Graph client. As the authentication provider, set an async lambda
// which uses the MSAL client to obtain an app-only access token to Microsoft Graph,
// and inserts this access token in the Authorization header of each API request.
GraphServiceClient graphServiceClient =
new GraphServiceClient(new DelegateAuthenticationProvider(async (requestMessage) =>
{
// Retrieve an access token for Microsoft Graph (gets a fresh token if needed).
var authResult = await confidentialClient
.AcquireTokenForClient(scopes)
.ExecuteAsync();
// Add the access token in the Authorization header of the API request.
requestMessage.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", authResult.AccessToken);
})
);
return graphServiceClient;
}
}
You can then create authenticated GraphServiceClients and use them to upload files, for example to SharePoint:
GraphServiceClient _graphServiceClient = await _graphAuthProvider.AuthenticateViaAppIdAndSecret(
tenantId,
clientId,
appSecret);
using (Stream fileStream = new FileStream(
fileLocation,
FileMode.Open,
FileAccess.Read))
{
resultDriveItem = await _graphServiceClient.Sites[sites[0]]
.Drives[driveId].Root.ItemWithPath(fileName).Content.Request().PutAsync<DriveItem>(fileStream);
}
Regarding the permissions: You may need more permissions than just Files.ReadWrite.All. As far as I know, an app needs the application permission Sites.ReadWrite.All to upload documents to SharePoint.
According to document : Upload or replace the contents of a DriveItem
If using client credential flow(M2M flow without user) , you should use below request :
PUT /drives/{drive-id}/items/{parent-id}:/{filename}:/content
Instead of :
https://graph.microsoft.com/v1.0/drive/items/{Id_Of_Specific_Folder}/proefdocument.txt/content
This the final working example using a GraphServiceClient
public async Task<DriveItem> UploadSmallFile(IFormFile file, bool uploadToSharePoint)
{
IFormFile fileToUpload = file;
Stream ms = new MemoryStream();
using (ms = new MemoryStream()) //this keeps the stream open
{
await fileToUpload.CopyToAsync(ms);
ms.Seek(0, SeekOrigin.Begin);
var buf2 = new byte[ms.Length];
ms.Read(buf2, 0, buf2.Length);
ms.Position = 0; // Very important!! to set the position at the beginning of the stream
GraphServiceClient _graphServiceClient = await AuthenticateViaAppIdAndSecret();
DriveItem uploadedFile = null;
if (uploadToSharePoint == true)
{
uploadedFile = (_graphServiceClient
.Sites["root"]
.Drives["{DriveId}"]
.Items["{Id_of_Targetfolder}"]
.ItemWithPath(fileToUpload.FileName)
.Content.Request()
.PutAsync<DriveItem>(ms)).Result;
}
else
{
// Upload to OneDrive (for Business)
uploadedFile = await _graphServiceClient
.Users["{Your_EmailAdress}"]
.Drive
.Root
.ItemWithPath(fileToUpload.FileName)
.Content.Request()
.PutAsync<DriveItem>(ms);
}
ms.Dispose(); //clears memory
return uploadedFile; //returns a DriveItem.
}
}
You can use a HttpClient as well
public async Task PostWebApi(string webApiUrl, string accessToken, IFormFile fileToUpload)
{
//Create a Stream and convert it to a required HttpContent-stream (StreamContent).
// Important is the using{...}. This keeps the stream open until processed
using (MemoryStream data = new MemoryStream())
{
await fileToUpload.CopyToAsync(data);
data.Seek(0, SeekOrigin.Begin);
var buf = new byte[data.Length];
data.Read(buf, 0, buf.Length);
data.Position = 0;
HttpContent content = new StreamContent(data);
if (!string.IsNullOrEmpty(accessToken))
{
// NO Headers other than the AccessToken should be added. If you do
// an Error 406 is returned (cannot process). So, no Content-Types, no Conentent-Dispositions
var defaultRequestHeaders = HttpClient.DefaultRequestHeaders;
defaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", accessToken);
HttpResponseMessage response = await HttpClient.PutAsync(webApiUrl, content);
if (response.IsSuccessStatusCode)
{
return;
}
else
{
// do something else
return;
}
}
content.Dispose();
data.Dispose();
} //einde using memorystream
}
}
Now I have configured for UWP photo post to web api part which is using HttpClient.
Uri uri = new Uri("http://localhost:50040/api/Upload");
IInputStream inputStream = await photoFile.OpenAsync(FileAccessMode.Read);
HttpMultipartFormDataContent multipartContent = new HttpMultipartFormDataContent();
multipartContent.Add(new HttpStreamContent(inputStream), "myFile", photoFile.Name);
Windows.Web.Http.HttpClient newclient = new Windows.Web.Http.HttpClient();
Windows.Web.Http.HttpResponseMessage response = await client.PostAsync(uri, multipartContent);
But I don't know how to set for the server side which is my .NET core web api to get the image which post from my UWP application.Please Help me, thank you.
But I don't know how to set for the server side which is my .NET core web api
Please reference the File uploads official tutorial to create your server side. For example, add POST method as following sample code showed to receive the UWP client sent file with the client code you showed above.
// POST api/values
[HttpPost]
public async Task<IActionResult> Post(IFormFile myFile)
{
// full path to file in temp location, you could change this
var filePath = Path.GetTempFileName();
if (myFile.Length > 0)
{
using (var stream = new FileStream(filePath, FileMode.Create))
{
await myFile.CopyToAsync(stream);
}
}
// process uploaded files
// Don't rely on or trust the FileName property without validation.
return Ok(new { filePath, myFile.Length });
}
More details you could also reference the official sample.
In Web API Controller
public IHostingEnvironment _environment;
public UploadFilesController(IHostingEnvironment environment) // Create Constructor
{
_environment = environment;
}
[HttpPost("UploadImages")]
public Task<ActionResult<string>> UploadImages([FromForm]List<IFormFile> allfiles)
{
string filepath = "";
foreach (var file in allfiles)
{
string extension = Path.GetExtension(file.FileName);
var upload = Path.Combine(_environment.ContentRootPath, "ImageFolderName");
if (!Directory.Exists(upload))
{
Directory.CreateDirectory(upload);
}
string FileName = Guid.NewGuid() + extension;
if (file.Length > 0)
{
using (var fileStream = new FileStream(Path.Combine(upload, FileName), FileMode.Create))
{
file.CopyTo(fileStream);
}
}
filepath = Path.Combine("ImageFolderName", FileName);
}
return Task.FromResult<ActionResult<string>>(filepath);
}
In yourpage.xaml.cs
using Windows.Storage;
using Windows.Storage.Pickers;
.....
StorageFile file;
......
private async void btnFileUpload_Click(object sender, RoutedEventArgs e) // Like Browse button
{
try
{
FileOpenPicker openPicker = new FileOpenPicker();
openPicker.ViewMode = PickerViewMode.Thumbnail;
openPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
openPicker.FileTypeFilter.Add(".jpg");
openPicker.FileTypeFilter.Add(".png");
file = await openPicker.PickSingleFileAsync();
if (file != null)
{
//fetch file details
}
}
catch (Exception ex)
{
}
}
//When upload file
var http = new HttpClient();
var formContent = new HttpMultipartFormDataContent();
var fileContent = new HttpStreamContent(await file.OpenReadAsync());
formContent.Add(fileContent, "allfiles", file.Name);
var response = await http.PostAsync(new Uri("Give API Path" + "UploadImages", formContent);
string filepath = Convert.ToString(response.Content); //Give path in which file is uploaded
Hope this code helps you...
But remember formContent.Add(fileContent, "allfiles", file.Name); line is important and allfiles is that name of parameter to fetch files in web api method "public Task<ActionResult<string>> UploadImages([FromForm]List<IFormFile> **allfiles**)"
Thanks!!!
I am trying to find an example of uploading a file to an Azure file share from a razor page. I would like to be able to select a file and then have that file saved to the share. I am using Visual Studio 2017, .Net Core 2.0. The only examples I am finding are for Blob storage. Any help would be much appreciated.
[HttpPost]
public IActionResult Index(Microsoft.AspNetCore.Http.IFormFile files)
{
string storageConnectionString = "connectionstring to your azure file share";
CloudStorageAccount cloudStorageAccount = CloudStorageAccount.Parse(storageConnectionString);
CloudFileClient cloudFileClient = cloudStorageAccount.CreateCloudFileClient();
CloudFileShare cloudFileShare = cloudFileClient.GetShareReference("your file share name");
cloudFileShare.CreateIfNotExistsAsync();
CloudFileDirectory rootDirectory = cloudFileShare.GetRootDirectoryReference();
CloudFile file = rootDirectory.GetFileReference(files.FileName);
TransferManager.Configurations.ParallelOperations = 64;
// Setup the transfer context and track the upoload progress
SingleTransferContext context = new SingleTransferContext();
using (Stream s1 = files.OpenReadStream())
{
var task = TransferManager.UploadAsync(s1, file);
task.Wait();
}
return RedirectToPage("/Index");
}
Here is a simple method I'm using to upload a single file to an endpoint.
[HttpPost]
public async Task<IActionResult> Upload(IFormFile file)
{
if (file != null)
{
using (var stream = new MemoryStream())
{
try
{
// assume a single file POST
await file.CopyToAsync(stream);
stream.Seek(0, SeekOrigin.Begin);
// now send up to Azure
var filename = file.FileName;
var storageAccount = CloudStorageAccount.Parse(<YOUR CREDS HERE>);
var client = storageAccount.CreateCloudFileClient();
var shareref = client.GetShareReference("YOUR FILES SHARE");
var rootdir = shareref.GetRootDirectoryReference();
var fileref = rootdir.GetFileReference(filename);
await fileref.DeleteIfExistsAsync();
await fileref.UploadFromStreamAsync(stream);
return Ok(new { fileuploaded = true });
}
catch (Exception ex)
{
return BadRequest(ex);
}
}
}
else
{
return BadRequest(new { error = "there was no uploaded file" });
}
}
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.