Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Downloaded images half broken in android #187

Open
codingslash76 opened this issue Oct 17, 2022 · 15 comments
Open

Downloaded images half broken in android #187

codingslash76 opened this issue Oct 17, 2022 · 15 comments

Comments

@codingslash76
Copy link

As mentioned in the issue.
joltup#130

We use react-native-blob-util to download images locally.

  • On IOS, It works as expected.
  • On Android, Images are half broken and it happens occasionally.

1

br

@RonRadtke
Copy link
Owner

Could you send the code you're using?
It works fine in my productive environment.
Did you check if your server is sending the whole file or if the connection e.g. is terminated during sending?

@codingslash76
Copy link
Author

export async function saveImageLocally(IMAGE_PATH) {
    try {
    const IMAGE_NAME = 'cached_image.png';

    const dirs = ReactNativeBlobUtil.fs.dirs.CacheDir;
  
    let PATH_OF_FILE = dirs + '/myproject/' + IMAGE_NAME;
    return ReactNativeBlobUtil.fs.exists(PATH_OF_FILE)
      .then((exist) => {
          
          if(exist) {
             return PATH_OF_FILE;
          } else {
            return ReactNativeBlobUtil.config({
              fileCache : true,
              path : dirs + '/myproject/'  + IMAGE_NAME
            })
            .fetch('GET', IMAGE_PATH)
            .then((res) => {
              let status = res.info().status;
              if (status == 200) {
                  return res.path();
              } else {
                  return null
              }
            });
          }
      })
    } catch (er) {
        return null
    }
}

@RonRadtke To your question

Did you check if your server is sending the whole file or if the connection e.g. is terminated during sending?

We use AWS S3 timeout image here. We have set the 1 min. timeout. It works fine on IOS with the same connection.

@RonRadtke
Copy link
Owner

Thank you.
Not sure how to reproduce it yet - so far I can't.

I would assume that the content length is somehow off and there for the image is only stored half.
You know if it is a chunked download?

@copiri-six
Copy link

I'm on the same team as @codingslash76, just wanted to offer some quick responses...

Thank you. Not sure how to reproduce it yet - so far I can't.

I know the feeling... we can't reproduce it on demand either. That's what is making this so frustrating. It doesn't appear to be related to the image storage source (AWS S3), as our other components (webapps, etc.) use the same API endpoints and load the images without issue 100% of the time. We typically test on a great connection as well, which makes a network hiccup unlikely at the frequency we're seeing the error.

I would assume that the content length is somehow off and there for the image is only stored half. You know if it is a chunked download?

No, it's not a chunked download. We're using AWS S3 for image storage, and AWS Presigned URLs with a 1-minute timeout for security. Here's the general workflow:

  • React Native app requests a data object, which includes a related image, from the API
  • API response with a JSON object, which includes an AWS Presigned URL for the image
  • React Native app uses this library to load the image... presumably, this is just a GET _image_url_ call
  • Image displays on load (or doesn't, occasionally)

Most images load in under a second, so the timeout shouldn't be an issue (and since it's not a chunked download, it shouldn't matter anyway as the security will either pass or fail for the full download). This is also the only place we're seeing an issue, so I don't think the presigned URLs are the problem in general.

Sorry to point the finger at your code, but we've got a pretty straightforward setup and are running out of ideas of what to test. 😬

Does it make a difference that the file can be displayed halfway? Meaning: the space required for the image at its full dimensions is blocked in the UI, it's just that only some part of the image actually loads. Is this different than if the image couldn't be displayed at all, or if the UI thought that the height of the image was only the visible/loaded section?

For us, this issue is compounded because we cache the images for reuse... so if any image only downloads partially, it's reused everywhere in its partially-visible state, which isn't fixed until it ages off. One workaround that would be interesting (although we haven't figured out how to do it yet) is to be able to identify when an image hasn't fully downloaded, and retry it (or delete it locally so it's reloaded on the next try). If you have any thoughts on that, they'd certainly be appreciated, even though I know it's not your job.

Thanks!!

@rajeshrasch
Copy link

rajeshrasch commented Nov 2, 2022

Guys, I am facing similar issue in IOS. Could any one was able to identify the root cause ?

@Dawood-Shahid
Copy link

Dawood-Shahid commented Nov 7, 2022

@codingslash76 you can reproduce this issue by killing your app before finishing the download. And you can track the download progress in below code block.

@Dawood-Shahid
Copy link

Dawood-Shahid commented Nov 7, 2022

export async function saveImageLocally(IMAGE_PATH) {
try {
const IMAGE_NAME = 'cached_image.png';
const dirs = ReactNativeBlobUtil.fs.dirs.CacheDir;
let PATH_OF_FILE = dirs + '/myproject/' + IMAGE_NAME;
return ReactNativeBlobUtil.fs.exists(PATH_OF_FILE)
.then((exist) => {
if(exist) {
return PATH_OF_FILE;
} else {
return ReactNativeBlobUtil.config({
fileCache : true,
path : dirs + '/myproject/' + IMAGE_NAME
})
.fetch('GET', IMAGE_PATH)
.progress({ count: 25 }, (received, total) => {
console.log('\n -------- > file: FileSystemManager.js > progress', {
received,
total,
});
})
.then((res) => {
let status = res.info().status;
if (status == 200) {
return res.path();
} else {
return null
}
});
}
})
} catch (er) {
return null
}
}

@Dawood-Shahid
Copy link

We can fix this issue by first downloading the file in the temp location and after successfully downloading we can move the file to our desired location

@Dawood-Shahid
Copy link

export async function saveImageLocally(IMAGE_PATH) {
try {
const IMAGE_NAME = 'cached_image.png';
const dirs = ReactNativeBlobUtil.fs.dirs.CacheDir;
let PATH_OF_FILE = dirs + '/myproject/' + IMAGE_NAME;
let TEMP_PATH_OF_FILE = dirs + '/myproject/' + IMAGE_NAME + '_temp';
return ReactNativeBlobUtil.fs.exists(PATH_OF_FILE)
.then((exist) => {
if(exist) {
return PATH_OF_FILE;
} else {
return ReactNativeBlobUtil.config({
fileCache : true,
path : TEMP_PATH_OF_FILE
})
.fetch('GET', IMAGE_PATH)
.then((res) => {
let status = res.info().status;
if (status == 200) {
ReactNativeBlobUtil.fs.mv(TEMP_PATH_OF_FILE, PATH_OF_FILE);
return PATH_OF_FILE;
} else {
return null
}
});
}
})
} catch (er) {
return null
}
}

@codingslash76
Copy link
Author

@Dawood-Shahid Thanks for your time here.

We can fix this issue by first downloading the file in the temp location and after successfully downloading we can move the file to our desired location

Is there a way to find out whether the image has been downloaded fully?
How can we assure here that the image downloaded in the temporary location is not a broken image?

@RonRadtke Did you find anything related to this? I am really stuck here. :(

@lclarkg18
Copy link

I'm experiencing this issue on iOS too (or at least one with the same symptoms). The problem in my case is related to the caching of the file, because the image appears correctly when I take the data from FetchBlobResponse and put it into react-native-file-viewer, but when I open the file that has been stored in path() (regardless of opening in finder or with ReactNativeBlobUtil) it is broken

@lclarkg18
Copy link

lclarkg18 commented Mar 10, 2023

Update: the issue only occurs when using the "path" option. If I let it set the path file by default (set to ReactNativeBlobUtil_tmp${timestamp} as mentioned in the docs) and specify the extension instead it works!! I've tried to rummage through the code in fetch.js but I couldn't find anything obvious, I understand that a lot of this is happening on a native level right @RonRadtke? (I get a bit lost outside of javascript)

@lclarkg18
Copy link

Update 2: I've narrowed down to the way that the file path is created. If you set the file path as:

      path: ReactNativeBlobUtil.fs.dirs.DocumentDir + `/${customFileName}`,

It doesn't work, whereas if you do this:

      path: ReactNativeBlobUtil.fs.dirs.DocumentDir + `${customFileName}`,

It does work, and it calls it Documents${customFileName} which is not ideal, but it's good enough for me for the time being.

@vgsnv
Copy link

vgsnv commented Oct 18, 2023

I also faced the same problem. For myself, I decided to use such a scheme, with publication (renaming) only if the file is exactly loaded.

const storeMediaToCache = async (params: {
  temporaryFileUriPart: string
  temporaryFileUriPublic: string
  url: string
}) => {
  const {temporaryFileUriPart, temporaryFileUriPublic, url} = params

  await ReactNativeBlobUtil.config({
    timeout: timeout,
    path: temporaryFileUriPart,
  })
    .fetch('GET', url, {
      'Content-Type': 'multipart/form-data',
      'Transfer-Encoding': 'Chunked',
    })
    .then(async (res) => {
      let status = res.info().status

      console.log('storeMediaToCache file received', url, temporaryFileUriPart, status)

      if (status == 200) {
        await ReactNativeBlobUtil?.fs.mv(temporaryFileUriPart, temporaryFileUriPublic)
        console.log('storeMediaToCache file rename to public', url, temporaryFileUriPublic)

        return temporaryFileUriPublic
      } else {
        console.log('storeMediaToCache ERRRRR', url, res)
        throw new Error('storeMediaToCache error')
      }
    })
    .catch((error) => {
      console.log('storeMediaToCache error', url, temporaryFileUriPublic, error)
      throw new Error('storeMediaToCache error')
    })
}

Should work

@codingslash76
Copy link
Author

@vgsnv
In your response, you suggested to move it after successful downloading. Sometimes broken images also return HTTP status 200.
How can we ensure that the image downloaded in the temporary location is not broken?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants