http icon indicating copy to clipboard operation
http copied to clipboard

FormData is not being send

Open FernetB opened this issue 4 years ago • 31 comments

Describe the bug I'm trying to do a post with a img file in a FormData and the request sends nothing

Steps to reproduce

const form = new FormData();
const file  = new File([someBlob], "name")

form.append("file", file)

Http.request({ data: form, headers: { "Content-type": "multipart/form-data" }, ...etc})

/* nothing is send, the form is empty */

Expected behavior To send the formData

Screenshots image

FernetB avatar Dec 28 '21 19:12 FernetB

Hey @FernetB. Have you considered using Http.uploadFile?

yarsdev avatar Dec 29 '21 14:12 yarsdev

Yes, but not only doesn't work but also ask the user for permissions to the file system.

FernetB avatar Dec 29 '21 14:12 FernetB

@thomasvidas I'm doing something wrong ? With axios this works perfectly, but I really need to use this plugin for set-cookie reasons with capacitor

FernetB avatar Dec 29 '21 15:12 FernetB

On web: image On Native: image

FernetB avatar Dec 30 '21 14:12 FernetB

Currently on Holiday, but I'm planning on fixing this in the 2.x branch; which isn't released yet. Http.request doesn't work with multipart form data. If someone else wants to make a PR I can review/release but I don't have the bandwidth to work on this.

thomasvidas avatar Dec 30 '21 18:12 thomasvidas

Can I use uploadFile for this? I tried and failed but maybe I did something wrong, or is just made for another purposes?

FernetB avatar Dec 30 '21 18:12 FernetB

I am having the same issue, is it fixed as yet?

christulsi avatar Jan 12 '22 18:01 christulsi

Nop, I have to change to axios and use a clouflare worker to handle the set-cookie and cors problems

FernetB avatar Jan 13 '22 00:01 FernetB

any updates?

vamfixed avatar Jan 17 '22 08:01 vamfixed

Any news ? Is there any way besides FormData to send multiple files at once ?

maudvd avatar Jan 25 '22 16:01 maudvd

Hi ! This is really a critical serious issue. It should be easy to use a form and send images with POST.

I won't let you all down, as I have a fix, but this issue should be resolved ASAP in my honest opinion.

So the fix idea is to get your image as dataURI in Ionic and send it as a string. This gives lot of problems really, and should be a fix only for a short period of time.

So, using this capacitor Http plugin, you will need first to convert the image in input to a DataURI. You will need fileReader and you will probably encounter another problem of capacitor, not loading the filereader properly.

So on Ionic app (interface side) you will need :

the fn to get fileReader

export function getFileReader(): FileReader {
  const fileReader = new FileReader();
  const zoneOriginalInstance = (fileReader as any)["__zone_symbol__originalInstance"];
  return zoneOriginalInstance || fileReader;
};

the fn to get the dataURI from fileReader

export function readURL(file: any){
    return new Promise((res, rej) => {
        const reader = getFileReader();
        reader.onload = e => res(e.target.result);
        reader.onerror = e => rej(e);
        reader.readAsDataURL(file);
    });
  };

the function to submit your data

  async submit(){
    const img = this.imgInput.nativeElement.files.item(0); // get the file from the input
    const dataURI = await readURL(img); // get the dataURI with readURL function
    await Http.request({ // post the file
      method: 'post',
      url: 'your/api/url/to/post/file',
      data : {img: dataURI}
      headers: {'Content-Type': 'application/json'}
    });
  }

of course you will also need to get the imgInput reference, so add the @ViewChild in your .ts file

  @ViewChild('imgInput' , { static: true }) imgInput: ElementRef<HTMLInputElement>;

on html side you will need the input and a button

<input #imgInput name="img" type="file" accept="image/*">
<button (click)="submit()">submit image</button>

Then on api side, you will need to get the string from the body and to convert it to a base64 data that can be written as a file.

you will need a getDataURI fn :

export function getDataURI(base64: string): {type: string , ext: string , data: string}{
    let type = '';
    let ext = '';
    let data = '';
    for(let i = 0 ; i < 60 ; i++){
      if(base64[i] === ':') {
        for(let j = 1 ; j < 60 ; j++) {
          if(base64[i+j] !== '/') {
            type = type + base64[i+j];
          } else {
            break;
          }
        }
      }
      if(base64[i] === '/') {
        for(let j = 1 ; j < 60 ; j++) {
          if(base64[i+j] !== ';') {
            ext = ext + base64[i+j];
          } else {
            break;
          }
        }
      }
      if(base64[i] === ',') {
        data = base64.substr(i + 1);
        break;
      }
    }
    return {type , ext , data}
  }

then you will have to get the data and write them using Buffer (on nodejs app)

      const infos = getDataURI(body.img); // we get the img from {img: dataURI} POST in ionic interface
      writeFileSync(resolve('./imgFile.jpg')  , Buffer.from(infos.data , 'base64')

Now with this you will probably get payload too large error if your server can't handle big POST requests (statusCode: 413)

I am personally using loopback 4 + dokku (so nginx). If you get this error, you will need to make your payload bigger on your server and on your app.

in my case, for nginx you need to add bigger body size :

sudo nano /etc/nginx/nginx.conf
// change line tp your needs => client_max_body_size 2M;
// for example client_max_body_size 200M;
// then save file and reload nginx
nginx -s reload

and then on loopback 4, in index.ts :

app.bind(RestBindings.REQUEST_BODY_PARSER_OPTIONS).to({limit: '200Mb'})

So yeah... this works but it's kinda horrible to just upload a file... And this will get out of hand with many files, you will have to handle a sort of lazy uploading. You could also try to stream files, or use websockets to send chunks... but yeah, this shouldn't be normal usage of Http imo.

mightytyphoon avatar Jan 27 '22 21:01 mightytyphoon

Currently on Holiday, but I'm planning on fixing this in the 2.x branch; which isn't released yet. Http.request doesn't work with multipart form data. If someone else wants to make a PR I can review/release but I don't have the bandwidth to work on this.

@thomasvidas Hi Thomas ! Could we have some infos about what could be wrong in the capacitor Http plugin not sending properly FormData with files using POST method ? So we can try to fix the problem and send a pull request ?

Thank you a lot.

mightytyphoon avatar Jan 28 '22 09:01 mightytyphoon

You can't send form data because it's not yet implemented. We need to wait until he or someone who knows Java and Swift adds it

El vie., 28 de enero de 2022 6:42 a. m., mightytyphoon < @.***> escribió:

Currently on Holiday, but I'm planning on fixing this in the 2.x branch; which isn't released yet. Http.request doesn't work with multipart form data. If someone else wants to make a PR I can review/release but I don't have the bandwidth to work on this.

@thomasvidas https://github.com/thomasvidas Hi Thomas ! Could we have some infos about what could be wrong in the capacitor Http plugin not sending properly FormData with files using POST method ? So we can try to fix the problem and send a pull request ?

Thank you a lot.

— Reply to this email directly, view it on GitHub https://github.com/capacitor-community/http/issues/213#issuecomment-1024045814, or unsubscribe https://github.com/notifications/unsubscribe-auth/ALJFPIBMCA3BQ7Z7LMXGPBTUYJQHRANCNFSM5K4V45UQ . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

You are receiving this because you were mentioned.Message ID: @.***>

FernetB avatar Jan 28 '22 14:01 FernetB

@FernetB I could give it a go. (I need to put the project I am working on in production in about 3 months, and I don't want to use my fix to send files in prod)

I bet for Android I need to do something with this file : https://github.com/capacitor-community/http/blob/main/android/src/main/java/com/getcapacitor/plugin/http/FormUploader.java and for iOS : https://github.com/capacitor-community/http/blob/main/ios/Plugin/HttpRequestHandler.swift

What I want is just someone to explain the possible problem(s) to try to fix it, this will help me to not lose too much time searching for the logic in the code.

I am into typescript and C++ everyday, but I am pretty language agnostic, so I could be able to understand how to send a formData using Java & Swift.

Here is a possible Java implementation : https://stackoverflow.com/questions/16797468/how-to-send-a-multipart-form-data-post-in-android-with-volley And here is a possible Swift implementation : https://stackoverflow.com/questions/43492089/how-to-send-form-data-in-post-request-in-swift-3

mightytyphoon avatar Jan 28 '22 16:01 mightytyphoon

Edit : Hi, because this repo is unfinished and seems already dead, I finally abandoned @capacitor/http and ended using Axios instead to be able to use http only cookies and post forms.

Bye.

mightytyphoon avatar Feb 05 '22 09:02 mightytyphoon

Hi, I finally ended using Axios to use http only cookies and post forms.

Bye.

When I've read this the first time (the "ended using axios" part) I thought it's something like "I'm done with axios, enough is enough". I understood it wrong and axios is the solution. I'm leaving this comment for other non native English speakers that may get confused as well.

EmilMatei avatar Feb 27 '22 19:02 EmilMatei

Will there be a fix for the FormData ? Axios is not a solution for some of us and using FormData seems like an essential part of a HTTP plugin.

maudvd avatar Feb 28 '22 09:02 maudvd

@modvd if you want to use cookies for persisting sessions and also formData, you don't have any other solution, check this repo, it's dead anyway. There is no more feedbacks on PR, only one guy has the hand on the repo and seems dead, so I don't think you should keep using this anyway. Either make your own or use Axios.

What I advise you to do is to create a http service inside your Ionic/Capacitor project, that will normalize the http requests. Meaning if one day this repo gets back on its feet, or you find something better than axios, you can only change one file to implement a new way of making the http requests in your app.

import { Injectable } from '@angular/core';
import { AxiosRequestConfig , default as http } from 'axios';

@Injectable({
  providedIn: 'root'
})
export class HttpService {

  constructor() { }

  public async get(url: string , config: AxiosRequestConfig){
    return await http.get(url , config);
  }

  public async post(url: string , body: any , config: AxiosRequestConfig){
    return await http.post(url , body , config);
  }

  public async delete(url: string , config: AxiosRequestConfig){
    return await http.delete(url , config);
  }

  public async head(){}
  public async put(){}

}

Of course this can be pumped up by adding typings, as well as Observable.

mightytyphoon avatar Feb 28 '22 13:02 mightytyphoon

Just got my whole app stop to function because i use, this lib as custom fetcher for Supabase, and they made the storage API use the custom fetcher last week. i will have a check and do a PR maybe next week if i have time Edit i didn't found a way to fix the module, and it's going to get deprecated in v4 in the meantime i found a solution for my use case in Supabase

export const useSupabase = () => {
  const options: SupabaseClientOptions = {
    fetch: (requestInfo, requestInit) => {
      const url = requestInfo.toString()
      if (requestInit?.method === 'POST' && url.includes('/storage/')) {
        return fetch(requestInfo, requestInit)
      }
      return Http.request({
        url,
        method: requestInit?.method,
        headers: requestInit?.headers as any || {},
        data: requestInit?.body,
      })
        .then((data) => {
          const type = data.headers['content-type']
          const res = type.includes('application/vnd.pgrst.object+json') ? data.data : JSON.stringify(data.data)
          const resp = new Response(res, {
            status: data.status,
            headers: data.headers,
          })
          return resp
        })
    },
  }
  return createClient(supabaseUrl, supabaseAnonKey, options)
}

riderx avatar Mar 08 '22 16:03 riderx

+1

nhh avatar Apr 25 '22 13:04 nhh

This is also a big problem for us as well. Has there been no progress yet?

@FernetB or @mightytyphoon Is there a Capacitor plugin that you used to implement Axios or just the normal JS lib?

MattSynaptic avatar Jun 07 '22 17:06 MattSynaptic

This is also a big problem for us as well. Has there been no progress yet?

@FernetB or @mightytyphoon Is there a Capacitor plugin that you used to implement Axios or just the normal JS lib?

Hi, I created a http service file that handles all http requests (patch, post, get, etc...) with parameters, for now I have just 3 apps using this, so I don't need to create a package for easy management but I would advise to do so. This way if one day you need to change http and use this plugin, in case it's patched, you can easily change only the file once and this will work on all your projects by a simple update.

And yes, axios, you simply need to install it in your project with npm. It works seemlessly, don't forget to use withCredentials=true to send cookies during authenticated requests.

mightytyphoon avatar Jun 07 '22 20:06 mightytyphoon

Axios was not a good solution for me in my Ionic app. I ended up going back to the cordova http plugin "cordova-plugin-advanced-http" which works great with FormData.

maudvd avatar Jun 08 '22 07:06 maudvd

Axios was not a good solution for me in my Ionic app. I ended up going back to the cordova http plugin "cordova-plugin-advanced-http" which works great with FormData.

Could you explain why, please ?

mightytyphoon avatar Jun 08 '22 10:06 mightytyphoon

Axios was not a good solution for me in my Ionic app. I ended up going back to the cordova http plugin "cordova-plugin-advanced-http" which works great with FormData.

Could you explain why, please ?

Because of the CORS restrictions when developing and running the app on a device.

maudvd avatar Jun 08 '22 12:06 maudvd

Axios was not a good solution for me in my Ionic app. I ended up going back to the cordova http plugin "cordova-plugin-advanced-http" which works great with FormData.

Could you explain why, please ?

Because of the CORS restrictions when developing and running the app on a device.

Oh yeah right, I had this problem also, you have to manage them yourself on server side. Can be annoying indeed but it works.

mightytyphoon avatar Jun 08 '22 13:06 mightytyphoon

I am facing same issue with this plugin. I tried implementing @ionic-native/http/ngx or cordova-plugin-advanced-http, project builds successfully. But in execution post methods doesn't get called or throws any exception.

Please fix capacitor-comminuty/http asap!!

rsushant avatar Jun 13 '22 11:06 rsushant

I am facing same issue with this plugin. I tried implementing @ionic-native/http/ngx or cordova-plugin-advanced-http, project builds successfully. But in execution post methods doesn't get called or throws any exception.

Please fix capacitor-comminuty/http asap!!

Would love to make a PR and fix this, (to do so you just need to code the formData functionality both under swift of iOS and java for android, then create the interface for execution under js/typescript), but repo master isn't responding at all nor looking at PRs...

So imo best way is to use axios, which works great, but as pointed out by @modvd you will need to manage CORS server side.

mightytyphoon avatar Jun 13 '22 12:06 mightytyphoon

@mightytyphoon @rsushant the repo admin is working on 100% compatible solution to integrate into Capacitor core as mentioned in the README : https://github.com/capacitor-community/http#maintence-mode Be patient better is coming :) There issue to follow also https://github.com/ionic-team/capacitor/issues/5145

riderx avatar Jun 15 '22 19:06 riderx

@mightytyphoon @rsushant the repo admin is working on 100% compatible solution to integrate into Capacitor core as mentioned in the README : https://github.com/capacitor-community/http#maintence-mode

Be patient better is coming :)

Yeah sure, the guy came like 4 months ago, just to put one line in The readme and ignore all messages and community's PRs...

mightytyphoon avatar Jun 15 '22 19:06 mightytyphoon