Using Expo.FileSystem to save images to disk, from zip file

I created an example repo of how to download a .zip file & unzip it: https://github.com/smoll/crna-zipfile

This works great for text files, but when I attempt to save an image (as a binarystring), it doesn’t seem to be a valid file. I had to set the uri to a base64 string in order to get the image to display (this code works):

https://github.com/smoll/crna-zipfile/blob/fdb41d5bce59812f4e47e9dc6c0abebf3ed509f2/App.js#L35-L50

Anyone have any idea how I could correctly save the image file to disk? FileSystem.writeAsStringAsync() only seems to work for text files. Here’s the documentation from JSZip that I was following along with:

@nikki any thoughts here?

Yeah right now the FileSystem API only supports plaintext content of files. One way to do this could be to do a to-from base64 encoding for binary files, or to use the upcoming Blob API. Do you think the base64-based API could work for your use case?

@nikki I think a base64-based approach could work, but can you provide a code example of what that looks like? Is there another function that allows me to read/write base64 encoded images?

Edit: not sure if this is what you meant, but I ended up storing my images in SQLite for now, as base64 string uris, as I alluded to above:

const b64uri = `data:image/jpeg;base64,${base64}`

I was struggling with the same problem when trying to generate Excel files and saving them to the device. I was able to work around the it by using FileSystem.downloadAsync. It’s meant for downloading files from external URLs but seems to work just fine with data URLs.

    FileSystem.downloadAsync(
      `data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,${content}`,
      fileUri,
    )
1 Like

I appreciate the idea on this @kaivosukeltaja, but all my testing of your suggestion results in an “unexpected url” error. I paste the same data URL into my browser and it works fine, so I can confirm that the data URI I’m testing with is properly formatted. I’m using Expo SDK 25. Any further thoughts?

I very much need a solution to this issue and really prefer not to detach. I have created an eReader that downloads zipped epubs, but cannot save any of the binary files to the system after I unzip the epub. My best solution to this point has been to redownload all the binary files directly with downloadAsync, but this takes forever and is not a viable final solution.

Yep, just noticed that it only works on iOS. I’m using the same SDK version and now again looking for a proper solution. A base64 based file save would work for me but I can’t find any information whether that’s currently possible or not.

@nikki, do we have any way to save a Blob to storage?
After fetching the image (I use my own fetch instead of FileSystem.downloadAsync because I have to set the authentication header) I can receive the blob but don’t know how to save to FileSystem.

Could you please help?

Hi @khoacoi - did you figure this out? I am looking at a similar problem… Thanks!

@ldwight Any luck with this? I am also running into the same problem as @khoacoi. Thanks!

Hi @medberg - my problem was slightly different- trying to save a file to a specific folder.
I basically gave up on that, but copy from the cache directory to the Documents directory and store the info in a SQLite db for retrieval.
If this is the kind of thing you are doing, let me know & I’ll share the particulars.
Yrs,
ldwight

Gave up, too :smiley:

Good news! As of version 31, this is easy.

import { FileSystem } from "expo"
import JSZip from "jszip"

// get zipData

const zip = await JSZip.loadAsync(zipData)

// create all necessary dirs

// write unzipped files 
zip.forEach((relativePath, file) => {
  if(file.dir) return

  const uri = `${localBaseUri}${relativePath}`

  file.async('base64').then(base64 => {
    FileSystem.writeAsStringAsync(uri, base64, {
       encoding: FileSystem.EncodingTypes.Base64,
     })
  })
})
2 Likes

hallo, could you give me a full source code from load zip file until unziping file?

Hi @andyhubert your solution doesn’t work because loadAsync from JSZIP expect to receive the zip file, if the zip is downloaded into the file system we need to use the Node fs module to read the file. We cannot use fs module directly in a managed expo app, for this case we need to use a native implementation like react-native-fs library, so we need to eject expo.

Hi @andyhubert . Your solution worked for me. Thanx.

zip.forEach(async (relativePath, file) => {
    if (file.dir) {
      //here is how i created the other dirs needed. it wont work if you don't create the dirs first
      let newDir = `${tilesDir}${file.name}`;
      await FileSystem.makeDirectoryAsync(newDir, { intermediates: true });

      return;
    }

    const uri = `${tilesDir}${relativePath}`;

    file.async("base64").then((base64) => {
      FileSystem.writeAsStringAsync(uri, base64, {
        encoding: FileSystem.EncodingType.Base64,
      });
    });
  });