I am currently trying to make a post request with a file attached to the form but I found out that it is not the file I have attached but just the path to the file.
My question is how do I get this file and attach it to the form?
Here is my code so far:
string altPath = Path.Combine(Application.persistentDataPath, "nice-work.wav");
List<IMultipartFormSection> formData = new List<IMultipartFormSection>();
formData.Add(new MultipartFormFileSection("wavfile", altPath));
UnityWebRequest uwr = UnityWebRequest.Post(url, formData);
yield return uwr.SendWebRequest();
if (uwr.isNetworkError)
{
Debug.Log("Error While Sending: " + uwr.error);
}
else
{
Debug.Log("Received: " + uwr.downloadHandler.text);
}
the variable altPath is the path but not the file and this leads to failed post request.
If you look at MultipartFormFileSection the constructor you are currently using is
MultipartFormFileSection(string data, string fileName)
which is of course not what you want to do.
You rather have to actually read the according file content e.g. simply using File.ReadAllBytes
...
var multiPartSectionName = "wavfile";
var fileName = "nice-work.wav";
var altPath = Path.Combine(Application.persistentDataPath, fileName);
var data = File.ReadAllBytes(altPath);
var formData = new List<IMultipartFormSection>
{
new MultipartFormFileSection(multiPartSectionName, data, fileName)
};
...
or depending on your server side needs
...
var formData = new List<IMultipartFormSection>
{
new MultipartFormFileSection(fileName, data)
};
...
Though have in mind that ReadAllBytes is a synchronous (blocking) call and for larger files you might rather want to use some asynchronous approach.
Related
Lets say, I upload really important files such a contracts via an API with HttpClient in .Net with the following code:
using (var content = new MultipartFormDataContent())
{
foreach (FileInfo fi in inputFiles)
{
content.Add(CreateFileContent(fi));
}
AwaitRateLimit();
var response = await _Client.PostAsync("upload/", content);
response.EnsureSuccessStatusCode();
// deserialize
string responseJson = await response.Content.ReadAsStringAsync();
ClientResponse.Response decodedResponse =
JsonSerializer.Deserialize<ClientResponse.Response>(responseJson);
}
private StreamContent CreateFileContent(FileInfo fileInfo)
{
var fileContent = new StreamContent(fileInfo.OpenRead());
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
{
Name = "\"file\"",
FileName = "\"" + fileInfo.Name + "\""
};
return fileContent;
}
Currently I do the following:
Send multiple files via post in one request
Download each file into ram, build sha256checksum of it and compare it against local files sha256checksum
step 1 is really quick (~a couple of seconds) for a large quantity of files
step 2 takes at least 15 minutes, because each file can only be downloaded individually.
Therefore I would know if you consider step 2 nessesary or if the HttpClient will handle that automatically.
I have a script that uploads a video to an API I built, and after it processes on the API side, a text file is returned to the client. The strange thing is, this only works with one type of file, a .QT file extension. Any other video type I try to send sends and empty video. I have tried .mov, .mp4, and .qt and only the .qt uploads properly. I'll post my code below. Would anyone know what cause only the one file type to work? Nothing on the API side singles out the qt file. I believe this is an issue with this script.
public async void Function() {
Debug.Log("works1");
string filePath = "IMG_0491.mov";
//string filePath = ProcessMode.theFilePath;
var client = new HttpClient();
using (var multipartFormContent = new MultipartFormDataContent()) {
//Add the file
Debug.Log("works2");
var fileStreamContent = new StreamContent(File.OpenRead(filePath));
Debug.Log("works3");
fileStreamContent.Headers.ContentType = new MediaTypeHeaderValue("video/mov");
multipartFormContent.Add(fileStreamContent, name: "file", fileName: filePath); //Originally Actual "Name`
//Send it
var response = await client.PostAsync("http://127.0.0.1:5000/", multipartFormContent); //Enter IP and Port of API when set up
Debug.Log("works4");
//Ensure it was successful.
response.EnsureSuccessStatusCode();
//Grab the animation data from the content.
var animation_data = await response.Content.ReadAsStringAsync();
Debug.Log(animation_data);
//Save to file.
//File.WriteAllTextAsync("AnimationFile.txt", animation_data);
await File.WriteAllTextAsync("AnimationFile.txt", animation_data);
Debug.Log("works5");
}
I've been trying to get uploading an image anonymously onto Imgur using the Imgur API to work, however I've been facing an issue with unauthorized path access.
I've tried search around other similar articles on Microsoft Docs and posts here on stack overflow but couldn't find a solution. I've even given my application "broadFileSystemAccess" as rescap capability in my Package.appxmanifest which I found from reading the Microsoft UWP documentations.
The error I receive is:
System.UnauthorizedAccessException: 'Access to the path 'C:\Users\lysyr\Pictures\ROG Logo.png' is denied.'
The error occurs at the var filecon = File.ReadAllBytes(imgpath); line.
My File Picker code is:
public static string imgpath = "";
public static string finalimg = "";
private async void FileNameButton_Click(object sender, RoutedEventArgs e)
{
var picker = new Windows.Storage.Pickers.FileOpenPicker();
picker.ViewMode = Windows.Storage.Pickers.PickerViewMode.Thumbnail;
picker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.PicturesLibrary;
picker.FileTypeFilter.Add(".jpg");
picker.FileTypeFilter.Add(".jpeg");
picker.FileTypeFilter.Add(".png");
Windows.Storage.StorageFile file = await picker.PickSingleFileAsync();
if (file != null)
{
// Application now has read/write access to the picked file
imgpath = file.Path;
var filecon = File.ReadAllBytes(imgpath); #Error drops here <---
finalimg = Convert.ToBase64String(filecon);
await ImgurUploadAPI();
Debug.WriteLine("Picked Image: " + file.Name);
uploadedimage_text.Text = "Picked Image: " + file.Name;
}
else
{
Debug.WriteLine("Image uploading has been cancelled.");
}
}
And the ImgurUpload task code is:
public static string imgurlink = "";
public async Task ImgurUploadAPI()
{
try
{
if (imgpath != null)
{
// Construct the HttpClient and Uri
HttpClient httpClient = new HttpClient();
Uri uri = new Uri("https://api.imgur.com/3/upload");
httpClient.DefaultRequestHeaders.Add("Authorization", "Client-ID IMGUR-CLIENTIDHERE");
//Debug.WriteLine("Request Headers: ");
// Construct the JSON to post
HttpStringContent content = new HttpStringContent("image=\"{finalimg}\"");
Debug.WriteLine("Request Upload: " + content);
// Post the JSON and wait for a response
HttpResponseMessage httpResponseMessage = await httpClient.PostAsync(
uri,
content);
// Make sure the post succeeded, and write out the response
httpResponseMessage.EnsureSuccessStatusCode();
var httpResponseBody = await httpResponseMessage.Content.ReadAsStringAsync();
imgurlink = httpResponseBody;
Debug.WriteLine("Request Response: " + httpResponseBody);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
}
I feel like it might be something to do with the way I access and convert the image to be ready for upload. Any tips would be very appreciated as I've been stuck on this for awhile now and it's one of the last things I need to complete for my project. Cheers!
Besides #Julian Silden Langlo answer. there is another option for your scenario. The reason for this behavior is that you could not directly access the file using File.ReadAllBytes(String) Method.
I noticed that you've already added the broadFileSystemAccess capability, what you need to note is that this capability only works for the Windows.Storage APIs. So you could only use it like
StorageFile file = StorageFile.GetFileFromPathAsync(filepath)
When you use UWP your access to the file system is limited. This means that you can't simply read a file at an arbitrary path like you're trying to do here:
var filecon = File.ReadAllBytes(imgpath);
What you need to do instead is to ask the StorageFile object you received from the FilePicker for a read Stream. Like so:
var buffer = await FileIO.ReadBufferAsync(file);
var filecon = buffer.ToArray();
finalimg = Convert.ToBase64String(filecon);
You can find more information about file access for UWP at Microsoft Docs.
I've built a new Unity project that makes use of C# and System.Net to access Imgur's API. I'm able to pull images into my application from anywhere, and I can upload screenshots to Imgur through my new application's client ID. I'm able to get a decent amount of response data on a successful upload, but the URL in the response is always outdated by 1. If I just uploaded Screenshot D, the URL I get back will link to Screenshot C, and so on.
Here is what the response data looks like:
This is the result I've gotten across all attempts, which includes using w.UploadValuesAsync() and in trying both Anonymous and OAuth2 (no callback) versions of Imgur applications.
Here is the bulk of my code, which is sourced from here.
public void UploadThatScreenshot()
{
StartCoroutine(AppScreenshotUpload());
}
IEnumerator AppScreenshotUpload()
{
yield return new WaitForEndOfFrame();
ScreenCapture.CaptureScreenshot(Application.persistentDataPath + filename);
//Make sure that the file save properly
float startTime = Time.time;
while (false == File.Exists(Application.persistentDataPath + filename))
{
if (Time.time - startTime > 5.0f)
{
yield break;
}
yield return null;
}
//Read the saved file back into bytes
byte[] rawImage = File.ReadAllBytes(Application.persistentDataPath + filename);
//Before we try uploading it to Imgur we need a Server Certificate Validation Callback
ServicePointManager.ServerCertificateValidationCallback = MyRemoteCertificateValidationCallback;
//Attempt to upload the image
using (var w = new WebClient())
{
string clientID = "a95bb1ad4f8bcb8";
w.Headers.Add("Authorization", "Client-ID " + clientID);
var values = new NameValueCollection
{
{ "image", Convert.ToBase64String(rawImage) },
{ "type", "base64" },
};
byte[] response = w.UploadValues("https://api.imgur.com/3/image.xml", values);
string returnResponse = XDocument.Load(new MemoryStream(response)).ToString();
Debug.Log(returnResponse);
}
}
Why is the URL I get back from w.UploadValues(...) always behind by one?
I have several export routes on an asp.net core application, but all of them are accessed by a GET request, eg: /api/{projectid}/{parameter}
These requests are generating xlsx files and sending them to the client. Now I have a similar request but I have to pass a long array to the method, so I would like to make it into a POST method and send the array (and other parameters) in the http body.
I get the correct response from the server (an array buffer starting with PK...) but I can't tell angular to save it as a file as I did with the similar GET requests. If I rewrite this back to start a GET request it works fine. What am I doing wrong?
Controller method:
[HttpPost("[action]")]
public IActionResult Export([FromBody] DistributionExportPostModel model)
{
var project = _ctx.Projects.FirstOrDefault(x=>x.Id == model.ProjectId);
byte[] xlsx = createXlsxFile(project, model.Selection, model.ComparisonBase);
var mimeType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
var fileName = $"TaskExport-"+project.Name+"-"+DateTime.Now.ToString("yyyyMMddHHmmss")+".xlsx";
return File(xlsx, mimeType, fileName);
}
Angular provider method:
export(projectid:string, selection:string[], comparisonBase:string):Promise<any[]> {
//let headers:Headers = new Headers();
//headers.append('Accept', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
//let params: URLSearchParams = new URLSearchParams();
let requestOptions = new RequestOptions();
requestOptions.responseType = ResponseContentType.ArrayBuffer;
return new Promise<any[]>((resolve) =>
this.http.post('/api/Distribution/Export', {
//'+projectid+'/'+comparisonBase+'/'+selection.join(','),
'ProjectId': projectid,
'Selection': selection,
'ComparisonBase': comparisonBase
}, requestOptions).subscribe((res) => {
window.open(res.url, '_blank');
resolve();
})
);
}
Below should work on Chrome.
var blob = new Blob(yourData, {type: "octet/stream"});
var url = window.URL.createObjectURL(blob);
a.href = url;
a.download = name;
a.click();
window.URL.revokeObjectURL(url);
For IE and Firefox, try this:
window.navigator.msSaveBlob(blob, filename);
You may need to add the anchor to the DOM before clicking it.