How to Write Asset Bundle to Disk

Hello, I’m to make the app I’m making download an asset bundle if it’s the first time, but save said bundle to disk,so it can check if it has already been downloaded and load it next time.

I’ve been looking forever, the only thing i could find was to save the downloadHandler.data to disk, like this,

        byte[] bytes = req.downloadHandler.data;

        File.WriteAllBytes(pathToSaveTo, bytes);

But Unity gives an error that you can’t access an asset bundler’s data directly?? I even tried generating a new byte array and copying the bytes over to no avail lol.

I’ve saved data from regular downloadHandlers just fine, I even have images saving to .png in the same game, but for some reason it doesn’t let you access the asset bundles download handler… I also looked up caching but i can’t really figure it out, nor do i really know if putting some 300mb of asset bundles in cache is really the best solution, as this will be on Amazon Firestick.

Here’s my script,

using System.IO;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using TMPro;
public class BookDownloader : MonoBehaviour
{
    string assetName, uRL, pathToSaveTo;
    UnityWebRequest r;
   
    void Start()
    {
        pathToSaveTo = Application.persistentDataPath + "/Books/";
        BookConfig config = GlobalValues.currentSelectedBookConfig;
        GetBookFromBundle(config.assetBundleUrl, config.bookName);
    }
    public void GetBookFromBundle(string uRL, string assetName)
    {
        this.uRL = uRL;
        this.assetName = assetName;
        string fileName = pathToSaveTo + assetName;
        if (!File.Exists(pathToSaveTo + assetName)) 
        {
            StartCoroutine(DownloadBookFromURL());
        }
        else
        {
            LoadManager.lm.SetLoadBarGoal(1);
            AssetBundleCreateRequest bundle = AssetBundle.LoadFromFileAsync(pathToSaveTo + assetName);
            GameObject book = bundle.assetBundle.LoadAsset<GameObject>(assetName);
            GlobalValues.currentBook = book;
            GlobalValues.currentBook.GetComponent<Book>().bookConfig.pagesText = GlobalValues.currentSelectedBookConfig.pagesText;
            StartCoroutine(DelayToStart());
        }
    }
    IEnumerator DownloadBookFromURL()
    {       
        r = UnityWebRequest.Get(uRL);
   
        StartCoroutine(GetDownloadProgress());
        yield return r.SendWebRequest();
        if (r.result == UnityWebRequest.Result.ConnectionError || r.result == UnityWebRequest.Result.DataProcessingError || r.result == UnityWebRequest.Result.ProtocolError)
        {
            StartCoroutine(DownloadBookFromURL());
            yield break;
        }
        if (!Directory.Exists(pathToSaveTo))
        {
            Directory.CreateDirectory(pathToSaveTo);
        }
        byte[] bytes = r.downloadHandler.data;
        File.WriteAllBytes(pathToSaveTo + assetName, bytes);
        AssetBundle bundle = AssetBundle.LoadFromFile(pathToSaveTo + assetName);
        Instantiate(bundle.LoadAsset<GameObject>(assetName));
        GameObject book = bundle.LoadAsset<GameObject>(assetName);
        GlobalValues.currentBook = book;
        GlobalValues.currentBook.GetComponent<Book>().bookConfig.pagesText = GlobalValues.currentSelectedBookConfig.pagesText;
        bundle.Unload(false);
        LoadManager.lm.downloadsFinished = true;
    }
    IEnumerator DelayToStart()
    {
        yield return new WaitForSeconds(3);
        LoadManager.lm.downloadsFinished = true;
    }
    IEnumerator GetDownloadProgress()
    {
       
        while (!r.isDone)
        {
            LoadManager.lm.SetLoadBarGoal(r.downloadProgress);
            yield return null;
        }
    }
}

Thanks

Edit: I’ve figured a work around, it seems kinda a band-aid fix but it works lol, but I’m using a regular UnityWebRequest, saving it to disk, then loading it from the disk.I’ve updated my script if anyone needs it. So weird that Unity doesn’t make it easy to access an asset bundle download handlers data, it doesn’t even allow reading the data!?

Asset bundle system has a built-in caching system. Maybe you want to use that?

1 Like

Sorry, I just updated my post with a work around i did. But I could not figure out how to get the cache system working, all the info i found was how to cache with the old WWW system. How do you cache it? But like I said in my post, i’m not sure if caching is a good idea, because it will be 300-500mb of data cached. And it needs to remain cached, because unless the books are updated(it’s a children’s book app), they should not be downloaded again. Or is caching pretty much the same as writing to disk?

Yes, caching does write to disk, but it may recompress the bundle for faster loading from cache.
To use caching you need to provide extra arguments to GetAssetBundle to enable caching.

1 Like

Okay, sorry, how do i go about caching exactly? So if I added those arguments, does it automatically cache? I was reading up on it and it seemed i needed to access the asset bundle’s manafest, and create a Hash and it was very confusing lol

You need to provide wither hash or crc and it will work out of the box:

You obtain them when you build the bundle. They are used for checking that the downloaded bundle is what you expect.

Sorry that’s what confused me. So do i need to do a separate web request to download the manifest as well?

Asset bundle manifest is optional, you can use bundles without it too. It is useful in certain situations.
More documentation here: Unity - Manual: AssetBundle workflow

1 Like