Jump to content

Api Endpoint - File Upload


Anthony Musgrove

Recommended Posts

Anthony Musgrove

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

chef

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 by chef
  • Like 1
Link to comment
Share on other sites

Anthony Musgrove

Thank you so much mate I really appreciate your help as always :)

Link to comment
Share on other sites

Anthony Musgrove

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

Anthony Musgrove

This part:

 

        //
        // Summary:
        //     Access to the multi-part/formdata files posted on this request
        IHttpFile[] Files { get; }
Link to comment
Share on other sites

Anthony Musgrove

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

Anthony Musgrove

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

Anthony Musgrove

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

Anthony Musgrove

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

Anthony Musgrove

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

Anthony Musgrove

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

Anthony Musgrove

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

Anthony Musgrove

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

Anthony Musgrove

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

Anthony Musgrove

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

Anthony Musgrove

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 by Anthony.Musgrove
Link to comment
Share on other sites

Anthony Musgrove

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 :/

 

5eca8ee2ba882_swaggerupload.png

Edited by Anthony.Musgrove
Link to comment
Share on other sites

Anthony Musgrove

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

Anthony Musgrove

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

Anthony Musgrove

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

PenkethBoy

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!

:)

  • Like 1
Link to comment
Share on other sites

chef

@@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 by chef
Link to comment
Share on other sites

Anthony Musgrove

 

@@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

Anthony Musgrove

It's coming together nicely now *grin*

 

5ecb4c436d84b_ScripterXPackageInstallerI

Link to comment
Share on other sites

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...