jaydenseric / apollo-upload-client

A terminating Apollo Link for Apollo Client that fetches a GraphQL multipart request if the GraphQL variables contain files (by default FileList, File, or Blob instances), or else fetches a regular GraphQL POST or GET request (depending on the config and GraphQL operation).
https://npm.im/apollo-upload-client
1.53k stars 155 forks source link

App Crash when using ReactNativeFile in apollo upload client #352

Closed amineoutmal closed 1 month ago

amineoutmal commented 1 month ago

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?

amineoutmal commented 1 month ago

+1 help

jaydenseric commented 1 month ago

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).

amineoutmal commented 1 month ago

Thanks for your response and your time. I understand that the ReactNativeFile class is no longer part of the package. However, I'm using an older version of this package, version 11, which still contains ReactNativeFile. I have come across some people who have successfully used this version with React Native. Additionally, I have seen a YouTube video demonstrating its usage live. I really need help with this.