aws-amplify / amplify-android

The fastest and easiest way to use AWS from your Android app.
https://docs.amplify.aws/lib/q/platform/android/
Apache License 2.0
244 stars 114 forks source link

Something went wrong with your AWS S3 Storage upload input stream operation - on some devices only #2408

Open mercierj opened 1 year ago

mercierj commented 1 year ago

Before opening, please confirm:

Language and Async Model

Kotlin - Coroutines

Amplify Categories

Storage

Gradle script dependencies

```groovy implementation 'com.amplifyframework:core-kotlin:2.7.1' implementation 'com.amplifyframework:aws-auth-cognito:2.7.1' implementation 'com.amplifyframework:aws-storage-s3:2.7.1' ```

Environment information

``` ------------------------------------------------------------ Gradle 8.0 ------------------------------------------------------------ Build time: 2023-02-13 13:15:21 UTC Revision: 62ab9b7c7f884426cf79fbedcf07658b2dbe9e97 Kotlin: 1.8.10 Groovy: 3.0.13 Ant: Apache Ant(TM) version 1.10.11 compiled on July 10 2021 JVM: 17.0.6 (JetBrains s.r.o. 17.0.6+0-17.0.6b802.4-9586694) OS: Mac OS X 13.0.1 x86_64 ```

Please include any relevant guides or documentation you're referencing

No response

Describe the bug

Hi ! I'm using the coroutines koltin sdk for uploading files (images/ video). Everything is working well on any devices tested on my side (physical & simulated), from any Android version (API 26 -> 33). My client located in French Polynesia, with a samsung A13 (Android 13), using wifi with good internet connection is not able to upload any file. He keeps getting "Something went wrong with your AWS S3 Storage upload input stream operation" right away. The content is either from his gallery or by capture intents. I'm using the same amplifyconfiguration.json on iOS & Android, and there is no bug on iOS side. Since I'm not able to reproduce this on my side, I don't have any logs for now. I would consider remote debuging but my client is not really available to help me with this task. Do you have any hint on what could cause this bug ? maybe a location related issue ? He is not using any simcard (but neither do I on my test devices). I don't have access to the backend unfortunately.

Thanks for your help, Regards

Reproduction steps (if applicable)

No response

Code Snippet

class UploadDataSource @Inject constructor(private val application: Application) {
    private val folder = "cache"

     @OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class)
  fun uploadFile(file: File) : Storage.InProgressStorageOperation<StorageUploadInputStreamResult> {
        val options = StorageUploadInputStreamOptions.builder()
            .accessLevel(StorageAccessLevel.PUBLIC).build()
         val uuid = "${UUID.randomUUID()}"
        val uploadFileName = "${uuid}.${file.extension}"
        val uploadPath = "$folder/$uploadFileName"
        return Amplify.Storage.uploadInputStream(uploadPath, application.contentResolver.openInputStream(file.toUri())!!, options)
    }
}

Log output

``` // Put your logs below this line ```

amplifyconfiguration.json

{
    "UserAgent": "aws-amplify-cli/2.0",
    "Version": "1.0",
    "auth": {
        "plugins": {
            "awsCognitoAuthPlugin": {
                "UserAgent": "aws-amplify/cli",
                "Version": "0.1.0",
                "IdentityManager": {
                    "Default": {}
                },
                "CredentialsProvider": {
                    "CognitoIdentity": {
                        "Default": {
                            "PoolId": "XXXXXXXXXX",
                            "Region": "eu-west-3"
                        }
                    }
                },
                "CognitoUserPool": {
                    "Default": {
                        "PoolId": "XXXXXXXXXX",
                        "AppClientId": "XXXXXXXXXX",
                        "Region": "eu-west-3"
                    }
                },
                "Auth": {
                    "Default": {
                        "authenticationFlowType": "USER_SRP_AUTH",
                        "socialProviders": [],
                        "usernameAttributes": [],
                        "signupAttributes": [
                            "EMAIL"
                        ],
                        "passwordProtectionSettings": {
                            "passwordPolicyMinLength": 8,
                            "passwordPolicyCharacters": []
                        },
                        "mfaConfiguration": "OFF",
                        "mfaTypes": [
                            "SMS"
                        ],
                        "verificationMechanisms": [
                            "EMAIL"
                        ]
                    }
                },
                "S3TransferUtility": {
                    "Default": {
                        "Bucket": "XXXXXXXXXX",
                        "Region": "eu-west-3"
                    }
                }
            }
        }
    },
    "storage": {
        "plugins": {
            "awsS3StoragePlugin": {
                "bucket": "XXXXXXXXXX"
                "region": "eu-west-3",
                "defaultAccessLevel": "guest"
            }
        }
    }
}

GraphQL Schema

```graphql // Put your schema below this line ```

Additional information and screenshots

device_specs

sdhuka commented 1 year ago

@mercierj One reason could be that he doesn't have all the permission granted because upload using input stream creates a temp file before uploading to S3, so I would start by confirming that. If the issue is still not fixed, then we would need logs to investigate further.

Also, a suggestion based on the code snippet you pasted, you can use uploadFile api directly if you are trying to upload a file. This will prevent redundant operation of converting file to inputstream and then again inputstream to temp file.

mercierj commented 1 year ago

Hi @sdhuka, thanks for your fast reply.

The user has authorized : CAMERA, RECORD_AUDIO, ACCESS_MEDIA_LOCATION, READ_MEDIA_VIDEOS, READ_MEDIA_IMAGES

I used the uploadFile before but I switched to inputStream trying to solve his bug, with same result unfortunately. No error except uploading through amplify when I manually copy a file from inputStream content provider for video trimming. I know ... I will try to get some logs 😅

sdhuka commented 1 year ago

@mercierj In that case, yes, please add logs and we will investigate further.

mercierj commented 1 year ago

Hi @sdhuka I've set up an alert which leads me to the cause : "the difference between the request time and the current time is too large". The phone my client is using has no simcard, I have deduced that it was related to his system clock. It seems like System. currentTimeMillis can be wrong on some phones without simcard. When he disabled the automatic timezone setting and he set manually any timezone it started to work. Maybe using another way to get timestamp than currentTimeMillis would fix this kind of problems ?

xaluxs commented 1 year ago

Hi, I have the same problem and one way to reproduce it is by maintaining the AS internal database inspector. When it is held open at 100% charge it is aborted and the status changes to failed.

Something went wrong with your AWS S3 Storage upload file operation

Captura de Pantalla 2023-04-25 a la(s) 7 52 10 p m

sdhuka commented 1 year ago

@mercierj Thanks for finding the root cause, could you please paste the stacktrace here so we can look into fixing the issue?

mercierj commented 1 year ago

I don't have access to the device or stacktrace, I had to debug remotely through some alert dialogs unfortunately.

DemiN0u commented 1 year ago

Same issue got on file upload. After first failed attempt all next attempts fail with error:

SinglePartUploadWorker failed with exception: java.io.IOException: kotlinx.coroutines.JobCancellationException: Parent job is Completed; job=JobImpl{Completed}@6458966
                                                                                                        at aws.smithy.kotlin.runtime.http.engine.okhttp.StreamingRequestBody.writeTo(StreamingRequestBody.kt:46)
                                                                                                        at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.kt:64)
                                                                                                        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
                                                                                                        at com.android.tools.profiler.agent.okhttp.OkHttp3Interceptor.intercept(OkHttp3Interceptor.java:57)
                                                                                                        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
                                                                                                        at com.android.tools.appinspection.network.okhttp.OkHttp3Interceptor.intercept(OkHttp3Interceptor.kt:52)
                                                                                                        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
                                                                                                        at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:34)
                                                                                                        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
                                                                                                        at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:95)
                                                                                                        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
                                                                                                        at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:84)
                                                                                                        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
                                                                                                        at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:65)
                                                                                                        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
                                                                                                        at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:205)
                                                                                                        at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:537)
                                                                                                        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1137)
                                                                                                        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637)
                                                                                                        at java.lang.Thread.run(Thread.java:1012)
                                                                                                    Caused by: kotlinx.coroutines.JobCancellationException: Parent job is Completed; job=JobImpl{Completed}@6458966
cyodrew commented 11 months ago

Hi @sdhuka I've set up an alert which leads me to the cause : "the difference between the request time and the current time is too large". The phone my client is using has no simcard, I have deduced that it was related to his system clock. It seems like System. currentTimeMillis can be wrong on some phones without simcard. When he disabled the automatic timezone setting and he set manually any timezone it started to work. Maybe using another way to get timestamp than currentTimeMillis would fix this kind of problems ?

I would like to second this problem, I have been getting this issue for a handful of uploads.

mercierj commented 11 months ago

I think it's related to System.currentTimeMillis used in the library. https://stackoverflow.com/questions/45509101/system-currenttimemillis-returns-incorrect-timestamp-on-huawei Using Instant.toEpochMilli() instead of System.currentTimeMillis() in the base code might fix the problem no ?

BadTokenException commented 8 months ago

I encountered the same problem. When a test mobile phone opens the VPN, it enters the program, then disconnects the VPN, and then uploads the file again. This error occurs. Even so, the error occurs again with the VPN. Restart the app after recovery.

fun upload( fileKey: String, fileStream: InputStream, onFailed: (Exception) -> Unit, onSuccess: (String) -> Unit ) { val option = StorageUploadInputStreamOptions.builder() .accessLevel(StorageAccessLevel.PUBLIC) .build()

Amplify.Storage.uploadInputStream(fileKey, fileStream, option,
    { result ->
        MSGLog.log("uploadS3 success:${result.key}")

    },
    { e ->
        e.printStackTrace()
    }
)

}