Anthony Musgrove 205 Posted May 23, 2020 Share Posted May 23, 2020 Hi all - are there any plugins out there that demonstrate creating an Api Endpoint to upload a file? I have been testing this for hours, and can't seem to get it. I created an Api Endpoint, which obviously works. It exists in my swaggerUI. However, I created a simple HTML document with a form to post a file for testing; <!DOCTYPE html> <html> <head><title></title></head> <body> <form name="frmUploadPackage" method="post" action="http://192.168.1.50:8096/emby/ScripterX/Packages/UploadPackage" enctype="multipart/form-data"> Choose package file: <input type="file" name="PkgFile" id="PkgFile"> <input type="submit" name="btnSubmit" value="Upload!"> </form> </body> </html> And when submitting, I basically use IRequiresRequest, and read the Request object: Request.Files is empty: 2020-05-24 00:49:34.654 Info Emby ScripterX: Total files on POST DATA = 0, content length = 455, content type=multipart/form-data; boundary=----WebKitFormBoundaryyL6Z6QQQCPN5zoDp [Route("/ScripterX/Packages/UploadPackage", "POST", Summary = "Upload a package to ScripterX Packages")] public void Post(PackageFile pFile) { Plugin._iLogger.Info("Total files on POST DATA = " + Request.Files.Length + ", content length = " + Request.ContentLength + ", content type=" + Request.ContentType); } What am I missing here! thank you. @@chef Link to comment Share on other sites More sharing options...
chef 3763 Posted May 23, 2020 Share Posted May 23, 2020 (edited) Yes, please see my Alexa endpoint plugin in GitHub Chefbennyj1 If you look under "Api/BackgroundImageService" You will find the answer. You must use the IReturn<object> And use the IHttpResultFactory. Specifically see the "emptyPng" image request Edited May 23, 2020 by chef 1 Link to comment Share on other sites More sharing options...
Anthony Musgrove 205 Posted May 23, 2020 Author Share Posted May 23, 2020 Thank you so much mate I really appreciate your help as always Link to comment Share on other sites More sharing options...
Anthony Musgrove 205 Posted May 24, 2020 Author Share Posted May 24, 2020 Hey mate, I checked out your code, it looks awesome for serving files out. I'm trying to get files in, like uploading a zip file to the emby server API rather than serving them out It's kinda odd that request files is empty even though I am posting the file to it. Link to comment Share on other sites More sharing options...
Anthony Musgrove 205 Posted May 24, 2020 Author Share Posted May 24, 2020 This part: // // Summary: // Access to the multi-part/formdata files posted on this request IHttpFile[] Files { get; } Link to comment Share on other sites More sharing options...
Anthony Musgrove 205 Posted May 24, 2020 Author Share Posted May 24, 2020 And I've checked the complete request coming from my browser test, and it seems very valid; POST /emby/ScripterX/Packages/UploadPackage HTTP/1.1 Host: 192.168.1.50:5999 Connection: keep-alive Content-Length: 455 Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 Origin: null Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryCWKRTMWE0OAxwgGf User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Accept-Encoding: gzip, deflate Accept-Language: en-US,en;q=0.9 ------WebKitFormBoundaryCWKRTMWE0OAxwgGf Content-Disposition: form-data; name="PkgFile"; filename="testpackage.zip" Content-Type: application/x-zip-compressed PK¸·P¿r(Utest123.txtthis is a test, 1 2 3.PK¸·P¿r(U test123.txtPK9? ------WebKitFormBoundaryCWKRTMWE0OAxwgGf Content-Disposition: form-data; name="btnSubmit" Upload! ------WebKitFormBoundaryCWKRTMWE0OAxwgGf-- Link to comment Share on other sites More sharing options...
Anthony Musgrove 205 Posted May 24, 2020 Author Share Posted May 24, 2020 Pulling my hair out here, hehe. I've just tried this too. // DataType = "Dictionary<string, string>", [Route("/ScripterX/Packages/UploadPackage", Verbs = "POST", Summary = "Upload a package to ScripterX Packages")] public class PackageFile : IReturnVoid { [ApiMember(Name = "TestFile", Description = "Package File", IsRequired = true, DataType = "IHttpFile", ParameterType = "body", Verb = "POST")] public IHttpFile TestFile { get; set; } } and- public void Post(PackageFile pFile) { Plugin._iLogger.Info("Api In > File = " + pFile.TestFile.FileName); } and pFile.TestFile is null. I'm posting via: <!DOCTYPE html> <html> <head><title></title></head> <body> <form name="frmUploadPackage" method="POST" action="http://192.168.1.50:8096/emby/ScripterX/Packages/UploadPackage" enctype="multipart/form-data"> Choose package file: <input type="file" name="TestFile" id="TestFile"> <input type="submit" name="btnSubmit" value="Upload!"> </form> </body> </html> It's an odd issue, unless Uploading Files are disabled in the API code? Link to comment Share on other sites More sharing options...
Anthony Musgrove 205 Posted May 24, 2020 Author Share Posted May 24, 2020 2020-05-24 17:01:33.477 Info HttpServer: HTTP Response 204 to 192.168.1.118. Time: 2ms. http://192.168.1.50:8096/emby/ScripterX/Packages/UploadPackage 2020-05-24 17:01:33.692 Info HttpServer: HTTP POST http://192.168.1.50:8096/emby/ScripterX/Packages/UploadPackage. UserAgent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36 2020-05-24 17:01:33.694 Info Emby ScripterX: Api File In > File Count = 0 Link to comment Share on other sites More sharing options...
Anthony Musgrove 205 Posted May 24, 2020 Author Share Posted May 24, 2020 So if I take out the enctype="multipart/form-data", and accept PkgFile as a string value, it gets the file's name: 2020-05-24 17:08:02.414 Info Emby ScripterX: Package File Not Null, it is = testpackage.zip Odd. Link to comment Share on other sites More sharing options...
Anthony Musgrove 205 Posted May 24, 2020 Author Share Posted May 24, 2020 And obviously when I remove the enctype on the HTML form, it just sends the data through as form data, rather than uploading the file to the endpoint. So it has to do with how the API code identifies the data being uploaded when it is a multipart/form-data request. POST /emby/ScripterX/Packages/UploadPackage HTTP/1.1 Host: 192.168.1.50:5999 Connection: keep-alive Content-Length: 43 Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 Origin: null Content-Type: application/x-www-form-urlencoded User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Accept-Encoding: gzip, deflate Accept-Language: en-US,en;q=0.9 PkgFile=testpackage.zip&btnSubmit=Upload%21 Link to comment Share on other sites More sharing options...
Anthony Musgrove 205 Posted May 24, 2020 Author Share Posted May 24, 2020 So I am thinking, instead of using ParameterType = "form", it probably needs to be ParameterType = "body" (because mutlipart/form-data is sent in the body of the request, not in form format). And some further investigation needs to go ahead. Link to comment Share on other sites More sharing options...
Anthony Musgrove 205 Posted May 24, 2020 Author Share Posted May 24, 2020 So essentially all this comes down to; how to match an ApiMember to the following data from the request body; ------WebKitFormBoundary8GU3x7O6xYtUJIMG Content-Disposition: form-data; name="PkgFiles[]"; filename="testpackage.zip" Content-Type: application/x-zip-compressed PK¸·P¿r(Utest123.txtthis is a test, 1 2 3.PK¸·P¿r(U test123.txtPK9? I have tried a multitude of options, the latest being: [ApiMember(Name = "PkgFiles", Description = "Package File", IsRequired = true, DataType = "Dictionary<string,object>", ParameterType = "form", Verb = "Post")] public Dictionary<string,object> PkgFiles { get; set; } However, PkgFiles is always null. It's not populating the variable. Link to comment Share on other sites More sharing options...
Anthony Musgrove 205 Posted May 24, 2020 Author Share Posted May 24, 2020 Request Headers exist ... 2020-05-24 18:01:04.150 Info Emby ScripterX: A HEADER: Host, value= 192.168.1.50:8096 2020-05-24 18:01:04.150 Info Emby ScripterX: A HEADER: Connection, value= keep-alive 2020-05-24 18:01:04.150 Info Emby ScripterX: A HEADER: Content-Length, value= 319921 2020-05-24 18:01:04.150 Info Emby ScripterX: A HEADER: Cache-Control, value= max-age=0 2020-05-24 18:01:04.150 Info Emby ScripterX: A HEADER: Upgrade-Insecure-Requests, value= 1 2020-05-24 18:01:04.150 Info Emby ScripterX: A HEADER: Origin, value= null 2020-05-24 18:01:04.150 Info Emby ScripterX: A HEADER: Content-Type, value= multipart/form-data; boundary=----WebKitFormBoundaryeUgXjRSBE0HNc6mN 2020-05-24 18:01:04.150 Info Emby ScripterX: A HEADER: User-Agent, value= Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36 2020-05-24 18:01:04.150 Info Emby ScripterX: A HEADER: Accept, value= text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 2020-05-24 18:01:04.150 Info Emby ScripterX: A HEADER: Accept-Encoding, value= gzip, deflate 2020-05-24 18:01:04.150 Info Emby ScripterX: A HEADER: Accept-Language, value= en-US,en;q=0.9 Link to comment Share on other sites More sharing options...
Anthony Musgrove 205 Posted May 24, 2020 Author Share Posted May 24, 2020 and I'm assuming to access the 'content' part of this request, would be: // // Summary: // Access to the multi-part/formdata files posted on this request IHttpFile[] Files { get; } However it is empty. It's like the IRequest isn't populating this information at all Link to comment Share on other sites More sharing options...
Anthony Musgrove 205 Posted May 24, 2020 Author Share Posted May 24, 2020 Even the InputStream. It's readable, (IRequiresRequestStream). When I read the contents, its empty. 2020-05-24 18:31:09.968 Info Emby ScripterX: REQUEST STREAM REQSTR IS POPULATED!, Readable? = True 2020-05-24 18:31:09.968 Info Emby ScripterX: Stream Data = ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ Link to comment Share on other sites More sharing options...
Anthony Musgrove 205 Posted May 24, 2020 Author Share Posted May 24, 2020 (edited) I think, still, that IRequest isn't populating .Files properly. It states that it should contain any Files submitted using multipart/form-data but it isn't being populated. @@Luke are you able to confirm that for me please? Because I think IRequest may be broken for posting files (multipart/form-data). (Without being able to see core source code for where the IRequest is fulfilled, I can't really debug or troubleshoot it) Thank you so much. Edited May 24, 2020 by Anthony.Musgrove Link to comment Share on other sites More sharing options...
Anthony Musgrove 205 Posted May 24, 2020 Author Share Posted May 24, 2020 (edited) So my latest is this (and it still isn't working): [Route("/ScripterXUpload", "Post", Summary = "Test Upload Function 2")] public class XUpload : IReturnVoid { [ApiMember(Name ="UploadFile", DataType = "IHttpFile", ParameterType = "body", AllowMultiple = true)] public IHttpFile UploadFile { get; set; } } public void Post(XUpload request) { if(request.UploadFile == null) { Plugin._iLogger.Info("UPLOAD FILE IS NULL :("); } Plugin._iLogger.Info("Upload File !!"); } <!DOCTYPE html> <html> <head><title></title></head> <body> <form name="frmUploadFile" method="POST" action="http://192.168.1.50:8096/emby/ScripterXUpload" enctype="multipart/form-data"> Choose package file: <input type="file" name="UploadFile"> <input type="submit" name="btnSubmit" value="Upload!"> </form> </body> </html> and output: 2020-05-25 01:09:20.448 Info HttpServer: HTTP POST http://192.168.1.50:8096/emby/ScripterXUpload. UserAgent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36 2020-05-25 01:09:20.498 Info Emby ScripterX: UPLOAD FILE IS NULL 2020-05-25 01:09:20.498 Info Emby ScripterX: Upload File !! 2020-05-25 01:09:20.498 Info HttpServer: HTTP Response 204 to 192.168.1.118. Time: 50ms. http://192.168.1.50:8096/emby/ScripterXUpload And I notice in SwaggerUI, the body content-type for some reason is application/json. I have a funny feeling that is also not correct :/ Edited May 24, 2020 by Anthony.Musgrove Link to comment Share on other sites More sharing options...
Anthony Musgrove 205 Posted May 24, 2020 Author Share Posted May 24, 2020 Also tried; //Content-Disposition: form-data; name="PkgFile"; filename="testpackage.zip" //Content-Type: application/x-zip-compressed [ApiMember(Name="name", DataType ="string", ParameterType = "body")] public string name { get; set; } Trying to get the name component from the ?body? of the request, that is null too Link to comment Share on other sites More sharing options...
Anthony Musgrove 205 Posted May 24, 2020 Author Share Posted May 24, 2020 A bit more output: 2020-05-25 01:58:28.971 Info Emby ScripterX: PathInfo: /emby/ScripterXUpload 2020-05-25 01:58:28.971 Info Emby ScripterX: Items Count: 0 2020-05-25 01:58:28.971 Info Emby ScripterX: QueryString: 2020-05-25 01:58:28.971 Info Emby ScripterX: RawURL: /emby/ScripterXUpload 2020-05-25 01:58:28.971 Info Emby ScripterX: Content Type: multipart/form-data; boundary=----WebKitFormBoundaryUpuDHxfQBCsxWOKE 2020-05-25 01:58:28.971 Info Emby ScripterX: Files Length: 0 2020-05-25 01:58:28.971 Info Emby ScripterX: Http Method: POST 2020-05-25 01:58:28.971 Info Emby ScripterX: Verb: POST achieved via: Plugin._iLogger.Info("PathInfo: " + Request.PathInfo); Plugin._iLogger.Info("Items Count: " + Request.Items.Count); Plugin._iLogger.Info("QueryString: " + Request.QueryString); Plugin._iLogger.Info("RawURL: " + Request.RawUrl); Plugin._iLogger.Info("Content Type: " + Request.ContentType); Plugin._iLogger.Info("Files Length: " + Request.Files.Length); Plugin._iLogger.Info("Http Method: " + Request.HttpMethod); Plugin._iLogger.Info("Verb: " + Request.Verb); Link to comment Share on other sites More sharing options...
Anthony Musgrove 205 Posted May 24, 2020 Author Share Posted May 24, 2020 I also noticed a typo in the summary for Request.Files; // // Summary: // Access to the multi-part/formdata files posted on this request IHttpFile[] Files { get; } which shows multi-part/formdata where it should be multipart/form-data I wonder if the processing for it is looking for the wrong content type because of a typo? Thanks guys, sorry to be a PITA. Link to comment Share on other sites More sharing options...
PenkethBoy 2066 Posted May 24, 2020 Share Posted May 24, 2020 Anthony to get Luke's attention - as this is a internal ApI issue is post in the Developer API sub forum he tends to respond to questions like this there and you can get some sleep! 1 Link to comment Share on other sites More sharing options...
Anthony Musgrove 205 Posted May 24, 2020 Author Share Posted May 24, 2020 Thank you so much mate. Sorry @@Luke Link to comment Share on other sites More sharing options...
chef 3763 Posted May 25, 2020 Share Posted May 25, 2020 (edited) @@Anthony.Musgrove Sorry I missed your replies. Does you're endpoint have: [Route("/YOUR_POST_ENDPOINT", "POST", Summary = "This End Point will accept the POST request")] public class MyPostRequest : IRequiresRequestStream { public Stream RequestStream { get; set; } //This is the image stream you can do something with } Edited May 25, 2020 by chef Link to comment Share on other sites More sharing options...
Anthony Musgrove 205 Posted May 25, 2020 Author Share Posted May 25, 2020 @@Anthony.Musgrove Sorry I missed your replies. Does you're endpoint have: [Route("/YOUR_POST_ENDPOINT", "POST", Summary = "This End Point will accept the POST request")] public class MyPostRequest : IRequiresRequestStream { public Stream RequestStream { get; set; } //This is the image stream you can do something with } Thats okay mate. I tried to use RequestStream -- and whenever I read out of it, all bytes are always null? Link to comment Share on other sites More sharing options...
Anthony Musgrove 205 Posted May 25, 2020 Author Share Posted May 25, 2020 It's coming together nicely now *grin* Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now