danielsogl / awesome-cordova-plugins

Native features for mobile apps built with Cordova/PhoneGap and open web technologies. Complete with TypeScript support.
https://danielsogl.gitbook.io/awesome-cordova-plugins/
Other
2.41k stars 2.43k forks source link

File Transfer plugin: .upload() returns "TypeError: res.json is not a function" inside .catch() #300

Closed WHarris22 closed 8 years ago

WHarris22 commented 8 years ago

I use the ion-native Transfer class to upload an image to an ASP.NET Web API which then receives the image and saves it in a directory (this all works fine). The API then returns a JSON object but the .upload() method always exits in the.catch(err) part of the promise and if I print out the error I just get

TypeError: res.json is not a function

I've tried to investigate this by inspecting the Transfer class but I can't see where the code is for when the plugin method gets wrapped into the promise.

Here is my Web API and Ionic code (sorry for the excessive amount of code, I just think that the more you see the more helpful it may be):

Web API

        [AllowAnonymous()]
        [HttpPost()]
        public IHttpActionResult CreateWithImage()
        {
            var files = HttpContext.Current.Request.Files;
            var issue = CreateIssueFromRequest(HttpContext.Current);
            if (files.Count > 0)
            {
                HttpPostedFile file = files[0];
                issue.ImageFilename = CreateFilename(file.FileName, issue.PostboxID);
                issue.ImagePath = SaveImage(file, issue.ImageFilename);
            }
            return Json(issueRepository.CreateIssue(issue));
        }

The CreateIssue method returns an object like this

var responseModel = new ApiResponseModel();
responseModel.IsSuccess = true;
responseModel.Message = "Issue submitted successfully";

issue-data-service.ts

createWithImage(_issue: Issue): Promise<any> {
        let _ft = new Transfer();
        let _filename = _issue.imagePath.substr(_issue.imagePath.lastIndexOf('/') + 1);
        console.log('Image file name: ' + _filename);
        let _options = {
            fileKey: 'file',
            fileName: _filename,
            mimeType: 'image/png',
            chunkedMode: false,
            httpMethod: 'POST',
            params: _issue.toJsonObject()
        };
        return _ft.upload(_issue.imagePath, this.apiBaseUrl + 'CreateWithImage', _options, false);
    }

issue-form.ts

this.issueService.createWithImage(this.issue).then((res: any) => {
                this.newIssue();
            }).catch((err: any) => {
                console.log(err.toString());
            });
WHarris22 commented 8 years ago

I think that wherever res.json() is being used, res is not the kind of object that supports the method.

I've reverted back to using the native plugin implementation and converting that to a promise myself and I have got it to work. Here is my implementation on the native plugin if it helps

issue-data-service.ts

createWithImage_Temporary(_issue: Issue): Promise<any> {
        return new Promise((resolve, reject) => {
            let _ft = new FileTransfer();
            let _filename = _issue.imagePath.substr(_issue.imagePath.lastIndexOf('/') + 1);
            console.log('Image file name: ' + _filename);
            let _options = {
                fileKey: 'file',
                fileName: _filename,
                mimeType: 'image/png',
                chunkedMode: false,
                httpMethod: 'POST',
                params: _issue.toJsonObject()
            };
            let onSuccess = function (result) { resolve(result); }
            let onError = function (error) { reject(error) }
            _ft.upload(_issue.imagePath, encodeURI(this.apiBaseUrl + 'CreateWithImage'), onSuccess, onError, _options, false);
        });        
    }

issue-form.ts

this.issueService.createWithImage_Temporary(this.issue).then((result: any) => {
                this.newIssue();
                let data = JSON.parse(result.response); 
                console.log('JSON parsed result.response = ' + data);
            }).catch((error) => {
                console.log('File Transfer Error: ' + error.toString());
            });
ihadeed commented 8 years ago

Thanks for opening the issue @WHarris22

Seems like the issue is with the wrapper then, I will investigate this soon while I build this app that will need to handle uploads as well.

When you call the upload() method, it's going through: CordovaInstance -> wrapInstance -> callInstance

It is possible that the successIndex and errorIndex I specified in the wrapper definition are not working as I expected.

ihadeed commented 8 years ago

Hey

I just used the plugin and it worked fine. The promise is resolving on success and rejecting on failure.

                        let upload_url: string = data.data.upload_url;
                        let image_url: string = data.data.public_url;
                        let transfer = new Transfer();
                        let options: FileUploadOptions = {
                          httpMethod: 'PUT',
                          chunkedMode: false,
                          headers: {
                            'Content-Type': 'image/jpeg'
                          }
                        };
                        transfer.upload(pic, upload_url, options)
                          .then(
                            () => updateAvatar(image_url),
                            (e)=>console.error(e)
                          );
WHarris22 commented 8 years ago

hmmm thats odd. Could it be anything to do with the version of ionic-native that was used? Back when I tested i was using version 1.2.4. I will test mine again once beta 11 is out and make sure I have the most up to date ionic-native installed too.

When you uploaded your image did your response contain any JSON data? I guess that could be another difference between our tests, not sure how much that may matter though

ihadeed commented 8 years ago

No my response didn't contain JSON data, but I don't think that has any effects.

The plugin decides to resolve/reject based on the HTTP status of the response, not based on the response body.

I suggest you use a tool like Postman to upload an image to your API and see what the response looks like.