apollographql / apollo-client

:rocket:  A fully-featured, production ready caching GraphQL client for every UI framework and GraphQL server.
https://apollographql.com/client
MIT License
19.34k stars 2.65k forks source link

App Crash when using ReactNativeFile in apollo upload client to upload file #11972

Closed amineoutmal closed 2 months ago

amineoutmal commented 2 months ago

Issue Description

I’m encountering a ClassNotFoundException related to okhttp3.internal.Util while trying to upload a file using ReactNativeFile with apollo-upload-client. Additionally, when using a Blob instance instead, the backend receives a null file argument, although the file upload works correctly when tested with Postman.

Steps to Reproduce

  1. Use DocumentPicker to select a document (e.g., PDF, DOC, DOCX, or image).
  2. Convert the selected file’s URI to a Blob instance.
  3. Create a File object with the Blob instance and attempt to upload it using uploadImageMutation from Apollo.

Using ReactNativeFile:

const handleDocumentSelection = async () => {
  try {
    const res = await DocumentPicker.pick({
      type: [
        DocumentPicker.types.pdf,
        DocumentPicker.types.doc,
        DocumentPicker.types.docx,
        DocumentPicker.types.images,
      ],
      copyTo:
        Platform.OS === 'android' ? 'cachesDirectory' : 'documentDirectory',
    });

    const uploadFile = async (fileUri, fileName, mimeType) => {
      const file = new ReactNativeFile({
        uri: fileUri,
        name: 'picture.jpg',
        type: mimeType
      });

      console.log('File object to be uploaded:', file);

      try {
        const { data } = await uploadImageMutation({
          variables: { file },
        });
        console.log('Upload response:', data);
      } catch (error) {
        console.error('Upload error:', error);
      }
    };

    const { fileCopyUri, name, type } = res[0];
    uploadFile(fileCopyUri, name, type);
  } catch (err) {
    if (DocumentPicker.isCancel(err)) {
      // Handle cancel
    } else {
      throw err;
    }
  }
};

### Using Blob Instance:

const handleDocumentSelection = async () => {
  try {
    const res = await DocumentPicker.pick({
      type: [
        DocumentPicker.types.pdf,
        DocumentPicker.types.doc,
        DocumentPicker.types.docx,
        DocumentPicker.types.images,
      ],
      copyTo:
        Platform.OS === 'android' ? 'cachesDirectory' : 'documentDirectory',
    });

    const convertUriToBlob = async (uri, mimeType) => {
      const base64Data = await RNFetchBlob.fs.readFile(uri, 'base64');
      const byteCharacters = atob(base64Data);
      const byteNumbers = new Array(byteCharacters.length);
      for (let i = 0; i < byteCharacters.length; i++) {
        byteNumbers[i] = byteCharacters.charCodeAt(i);
      }
      const byteArray = new Uint8Array(byteNumbers);
      return new Blob([byteArray], { type: mimeType });
    };

    const uploadFile = async (fileUri, fileName, mimeType) => {
      const fileBlob = await convertUriToBlob(fileUri, mimeType);

      // Create a file object with a Blob instance
      const file = new File([fileBlob], fileName, { type: mimeType });

      console.log('File object to be uploaded:', file);

      try {
        const { data } = await uploadImageMutation({
          variables: { file },
        });
        console.log('Upload response:', data);
      } catch (error) {
        console.error('Upload error:', error);
      }
    };

    const { fileCopyUri, name, type } = res[0];
    uploadFile(fileCopyUri, name, type);
  } catch (err) {
    if (DocumentPicker.isCancel(err)) {
      // Handle cancel
    } else {
      throw err;
    }
  }
};

### Crash Log Caused by: java.lang.ClassNotFoundException: Didn't find class "okhttp3.internal.Util" on path: DexPathList[[zip file "/data/app/~~6kd2JTp1gbPoLfrIrCABCA==/care.heyme.mobile-MkqSROqHXIAMbKVAbAvP-w==/base.apk"],nativeLibraryDirectories=[/data/app/~~6kd2JTp1gbPoLfrIrCABCA==/care.heyme.mobile-MkqSROqHXIAMbKVAbAvP-w==/lib/arm64, /data/app/~~6kd2JTp1gbPoLfrIrCABCA==/care.heyme.mobile-MkqSROqHXIAMbKVAbAvP-w==/base.apk!/lib/arm64-v8a, /system/lib64, /product_h/lib64, /system_ext/lib64]]

System Information

macOS: 14.1.1 Node: 16.0.0 npm: 7.10.0 React Native: 0.68.1 Apollo Upload Client: 11.0.0 Android SDK: API Levels 30, 31, 33, 34, 35 Android Studio: 2024.1 Java: 17.0.11

### Additional Information

The problem persists despite using the latest version of apollo-upload-client. The backend receives a null file argument when using the Blob instance, but the upload works correctly in Postman.

Request for Help

Can anyone assist with resolving this issue? Are there known compatibility problems or configuration settings that could address this ClassNotFoundException and the issue with the Blob instance?

Link to Reproduction

d

Reproduction Steps

  1. Use DocumentPicker to select a document (e.g., PDF, DOC, DOCX, or image).
  2. Convert the selected file’s URI to a Blob instance.
  3. Create a File object with the Blob instance and attempt to upload it using uploadImageMutation from Apollo.

@apollo/client version

3.8.7

jerelmiller commented 2 months ago

Hey @amineoutmal 👋

Have you tried opening an issue with apollo-upload-client? This is not a library we own so I'm not sure there is much we can do here if you're seeing failures from that link.

amineoutmal commented 2 months ago

@jerelmiller hey thank you for your response , yeah already put the same issue the author replay with :

If you are having trouble with React Native, or your Java tooling, then you need to ask those respective communities for help. I only target standard JavaScript runtime environments for this package, and no longer explicitly make attempts to support a special non-standard React Native environment; the class ReactNativeFile no longer exists in this package (although as you're likely aware, you should be able to create something similar in your own React Native project code).

anyway , can you guide me how to implement upload file without using this library ? is apollo client support multipart/form-data for uploading file ?

jerelmiller commented 2 months ago

@amineoutmal I'd honestly use apollo-upload-client as a starting point, either by forking it or using the implementation as a guide. Your link just needs to be the terminating link in your link chain since it sends the requests to your server.

is apollo client support multipart/form-data for uploading file

Apollo Client is agnostic to the transport or content type. Its the responsibility of the terminating link to handle what kind of request it sends. This allows you to implement the link however you want, using whatever transport or content type you want, as long as its able to send/receive requests and report that back through the link chain 🙂.

I don't think there is anything actionable for us to do here, so I'm going to go ahead and close this. If you have additional questions or need help, feel free to reach out on our discord channel and we'd be happy to help. Best of luck!

github-actions[bot] commented 2 months ago

Do you have any feedback for the maintainers? Please tell us by taking a one-minute survey. Your responses will help us understand Apollo Client usage and allow us to serve you better.

github-actions[bot] commented 4 weeks ago

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. For general questions, we recommend using StackOverflow or our discord server.