imagekit-developer/imagekit-javascript

Uploading multiple images on iOS devices failing

Closed this issue · 11 comments

Hi,

We are getting the following error when uploading files from Safari (using client side upload with the JS SDK:

"The token with value '8cbdb59b-6246-4f8f-b0e0-77b34c4e9e63' has been used before. We suggest using V4 UUIDs, or another random string with enough entropy to avoid collisions.”

The first file (in a list) will work, while the following fail. This error is only occurring on iOS devices.

Near identical issue (however it's using Uppy):
imagekit-developer/imagekit-uppy-plugin#3

After some debugging I have noticed that on an Android device, or a computer, running a for loop to upload images ImageKit will call the our cloud function to retrieve authenticationParameters for every single iteration, however, on an iOS device the cloud function will only be called on the first iteration. Based on the issue above, it seems like it's caching the result on iOS so it doesn't need to make the same call again. Issue is this is causing subsequent uploads to fail......

A potential fix could be to do the following after line 68 at

xhr.open('GET', authenticationEndpoint);

xhr.setRequestHeader('Cache-Control', 'no-cache');`

Edit:
After much trail and error I have found the only way to solve the issue is by making the call to authenticationEndpoint a POST request. I have tried adding every form of cache control header (both on server & client), however Safari ignores all of them.

Suggested fix: Change API call to authenticationEndpoint from GET to POST request

Code for reference:

Initialise Imagekit:

new ImageKit({
publicKey: "PUBLIC_KEY",
urlEndpoint: "URL_ENDPOINT",
authenticationEndpoint: "API_ENDPOINT,
}),

Authentication endpoint (cloud function):

exports.createImageKitSignature = functions
.https.onRequest((req, res) => {
cors(req, res, () => {
const authenticationParameters = imagekit.getAuthenticationParameters()
res.status(200).send(authenticationParameters)
})
})

Upload function:

async uploadImage({ state }, file) {
  try {
    const result = await imagekit.upload({file: file, fileName: file.name })
    console.log(result)
    return result      
  } catch (error) {
    console.log('error occured', error)
  }

Could you please share a working (minimum) code for me to reproduce this issue?

I've attached a minimum code working example.

  1. Enter required values on .env file
  2. yarn install
  3. yarn serve
  4. open Safari
  5. Open network tab to see XHR requests
  6. click the upload files input button, select 5 - 10 images

You'll then see the mentioned errors. Also note the network tab shows only 1 call to the server (for all 5-10 images)

You can also then try this on Google Chrome and see that it works as intended, on the network tab you will see 1 call to the server for every image uploaded

vuejs.zip

Got the issue reproduced, Safari just doesn't seem to respect any header.
Instead of POST, what I have done is added a random query param in the GET request itself. This is to prevent any breaking change in existing implementations.

Before I release the SDK, can you confirm if the below bundle works for you? Its only for testing, please do not use it in production.

https://ik.imagekit.io/demo/img/imagekit.min_ZkfCXeHcx.js?ik-sdk-version=javascript-1.4.3&updatedAt=1656997731049

Hi, I've tried using the script above, the problem still exists. Perhaps make some configuration options to allow requestType to be post? This way it won't be such a breaking change.

It is not possible to cache anymore because the URL is different. Can you share the code with the above code bundle not working?

Exact same bundle I uploaded before with the following changes:

Added to index.html:

<script type="text/javascript" src="https://ik.imagekit.io/demo/img/imagekit.min_ZkfCXeHcx.js?ik-sdk-version=javascript-1.4.3&updatedAt=1656997731049"></script>

Removed "import ImageKit from "imagekit-javascript" from HelloWorld.vue (as we're using the CDN script)

The rest remains the same:
`
export default {
name: "HelloWorld",
props: {
msg: String
},
methods: {
handleFiles(e) {
const files = e.target.files
for (let i = 0; i < files.length; i++) {
this.uploadImage(files[i])
}
},
async uploadImage(file) {
// eslint-disable-next-line no-undef
const imagekit = new ImageKit({
urlEndpoint: process.env.VUE_APP_IMAGEKIT_URL_ENDPOINT,
publicKey: process.env.VUE_APP_IMAGEKIT_PUBLIC_KEY,
authenticationEndpoint: process.env.VUE_APP_YOUR_AUTH_ENDPOINT || "http://localhost:3001/auth"
})

  try {
    const result = await imagekit.upload({ file: file, fileName: file.name })
    console.log('result', result)
  } catch (error) {
    console.log('error', error)
  }
}

}
};
`

Its working, I selected 4 files and browser issues 4 auth requests
image
there is a random query param at the end
image

The problem still persists on my end, I have noticed however the new script is an improvement, on the original package, it will make 1 or 2 network calls at most until cached (test with 8 images), on the new package it made 5 network calls (test with 8 images) until it cached (not sure how it's caching with randomised URL's?).

With original package:

Screen Shot 2022-07-05 at 4 12 31 pm

With new script:

Screen Shot 2022-07-05 at 4 01 56 pm

The new bundle worked as intended on both Google Chrome & Safari. Haven't been able to test on an iOS device yet but it seems to be right.

Fixed in 1.5.2