A few moments ago I posted a question because I wasn't able to download a file using WWW() but now I've come to a new problem:
The file that has to be downloaded is ~127mb in size. When I check the file that has been downloaded it shows 55mb. (Which explains why the file isn't able to be opened).
The code I'm using to download the file:
IEnumerator WaitForGame(){
WWW www = new WWW("https://www.dropbox.com/sh/aayo9iud7t98hgb/AACDqSST_-rT2jIfxq1Zc2SXa?dl=1");
Debug.Log ("Downloading");
yield return www;
byte[] yourBytes = www.bytes;
System.IO.File.WriteAllBytes(FullPath, yourBytes);
Debug.Log ("Done downloading!");
}
void UpdateGame(){
string path = Application.dataPath;
if (Application.platform == RuntimePlatform.OSXPlayer) {
path += "/../../";
}
else if (Application.platform == RuntimePlatform.WindowsPlayer) {
path += "/../";
}
if (System.IO.File.Exists (path+"/Apo_Alpha.app")) {
Debug.Log ("File exists!");
System.IO.File.Delete (path+"/Apo_Alpha.app");
}
FullPath = path + "/Apo_Alpha.app";
StartCoroutine (WaitForGame ());
}
I have no clue to what the problem can be, and it would be great if someone could help me out with this one.
Related
I am trying to figure out how to stop UnityWebRequest download immediately with a button in android application.
Stopping coroutine with StopCoroutine("downLoadFromServer"); does not stop UnityWebRequest.
I tried using UnityWebRequest.Dispose(); or UnityWebRequest.Abort(); with no success.
Below is the download i'm trying to stop.
private IEnumerator downLoadFromServer()
{
var url = "https://example.com/app.apk";
var savePath = Path.Combine(Application.persistentDataPath, "data", "app.apk");
using (var uwr = UnityWebRequest.Get(url))
{
uwr.SetRequestHeader("Authorization", "Basic " + System.Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes("test:test")));
uwr.SendWebRequest();
while (!uwr.isDone)
{
progressText.text = $"Progress: {uwr.downloadProgress:P}";
yield return null;
}
var yourBytes = uwr.downloadHandler.data;
progressText.text = $"Done downloading. Size: {yourBytes.Length}";
//Create Directory if it does not exist
var directoryName = Path.GetDirectoryName(savePath);
if (!Directory.Exists(directoryName))
{
Directory.CreateDirectory(directoryName);
progressText.text = "Created Dir";
}
try
{
//Now Save it
System.IO.File.WriteAllBytes(savePath, yourBytes);
Debug.Log("Saved Data to: " + savePath.Replace("/", "\\"));
progressText.text = "Saved Data";
}
catch (Exception e)
{
Debug.LogWarning("Failed To Save Data to: " + savePath.Replace("/", "\\"));
Debug.LogWarning("Error: " + e.Message);
progressText.text = "Error Saving Data";
}
}
//Install APK
installApp(savePath);
}
Thank you,
Chris
You are right: Stopping the Coroutine does not cancel the UnityWebRequest, you are just not keeping track of it anymore.
Actually UnityWebRequest.Abort should do exactly that
If in progress, halts the UnityWebRequest as soon as possible.
This method may be called at any time. If the UnityWebRequest has not already completed, the UnityWebRequest will halt uploading or downloading data as soon as possible. Aborted UnityWebRequests are considered to have encountered a system error. Either the isNetworkError or the isHttpError property will return true and the error property will be "User Aborted".
However this makes ofcourse only sense if you actually check the result of the request which currently you aren't doing and you should do anyway!
A way to use it would be to make the uwr a class wide field so you can access it from somewhere else and as said actually add a check if the download succeeded:
private UnityWebRequest uwr;
private IEnumerator downLoadFromServer()
{
...
// NOTE: Remove the var so the class field uwr is assigned to, not only
// a local variable
using(uwr = UnityWebRequest.Get(...))
{
...
// Always check if a request was actually successful before continuing
if(uwr.isHttpError || uwr.isNetworkError || !string.IsNullOrWhiteSpace(uwr.error))
{
Debug.LogWarning($"Download Failed with {uwr.responseCode}, reason: {uwr.error}", this);
progressText.text = $"Download Failed: {uwr.error}";
// Cancel this Coroutine
yield break;
}
var yourBytes = uwr.downloadHandler.data;
...
}
//Install APK
installApp(savePath);
}
Then you can simply do
public void AbortDownload()
{
if(uwr != null && !uwr.isDone)
{
Debug.Log("Aborting download ...", this);
uwr.Abort();
}
else
{
Debug.Log("Not downloading, nothing to do ...", this);
}
}
which should make the download "fail" and thereby also finish the Coroutine
In my script im expecting the text bytesDownloadedText to be updated with how many bytes have been downloaded so far but it only runs once and stays on 0. How do I fix this
private IEnumerator DownloadFile(){
WWW w = new WWW (PATH_TO_DOWNLOAD);
bytesDownloadedText.text = w.bytesDownloaded.ToString();
yield return w;
if (w.error != null) {
Debug.LogError ("Error: " + w.error);
} else {
scriptText = w.text;
filesDownloaded = true;
Debug.Log (scriptText);
}
}
Edit: New code + Additional debugging information
private IEnumerator DownloadFile(){
WWW w = new WWW (PATH_TO_DOWNLOAD);
while(!w.isDone){
bytesDownloadedText.text = w.bytesDownloaded.ToString ();
Debug.Log ("Bytes Downloaded: " + w.bytesDownloaded);
yield return null;
}
Debug.Log ("Exiting while loop");
if (w.error != null) {
Debug.LogError ("Error: " + w.error);
} else {
//bytesDownloadedText.text = w.bytesDownloaded.ToString ();
scriptText = w.text;
filesDownloaded = true;
Debug.Log (scriptText);
}
}
Filesize: 337 bytes
Download Path: https://deathcrow.altervista.org/websharp/files.php
3.Code where DownloadFile is called
private void Start(){
StartCoroutine (DownloadFile ());
}
The while loop is exiting
w.text returns the test script from the server(which is code)
using UnityEngine;
public class TestScript: MonoBehaviour{
public void Update(){
if (Input.GetMouseButtonDown (0)) {
RaycastHit hit;
if (Physics.Raycast (Camera.main.ScreenPointToRay(Input.mousePosition),out hit)){
if (hit.transform.tag == "Destroy") {
Destroy (hit.transform.root.gameObject);
}
}
}
}
}
Edit: Online code
<?
$scriptText = "";
$file_handle = fopen("TestScript.cs","r");
while(!feof($file_handle)){
$line = fgets($file_handle);
$scriptText = $scriptText . $line;
}
fclose($file_handle);
$size = strlen($scriptText);
header("Content-length: ".$size);
echo $scriptText;
?>
Do not yield WWW if you want to use the bytesDownloaded property as that will pause your code until WWW returns which makes it impossible to read how much data has been downloaded.
You have to put WWW.bytesDownloaded in a loop then use WWW.isDone to detect when WWW is done and then exit the loop. Inside that loop, you can use WWW.bytesDownloaded to display the downloaded data. Finally, you must wait for a frame after each loop so that other scripts can execute or Unity will freeze until the download is done.
This is what that code should look like:
private IEnumerator DownloadFile()
{
WWW w = new WWW(PATH_TO_DOWNLOAD);
while (!w.isDone)
{
yield return null;
bytesDownloadedText.text = w.bytesDownloaded.ToString();
Debug.Log("Bytes Downloaded: " + w.bytesDownloaded);
}
if (w.error != null)
{
Debug.LogError("Error: " + w.error);
}
else
{
scriptText = w.text;
filesDownloaded = true;
Debug.Log(scriptText);
}
}
Note: There are some instances where the bytesDownloaded property returns 0. This has nothing to do with Unity. This happens mostly when your server is not sending the Content-Length header.
Example of sending the Content-Length header from the server(php):
<?php
//String to send
$data = "Test message to send";
//Get size
$size= strlen($data);
//Set Content-length header
header("Content-length: ".$size);
//Finally, you can send the data
echo $data;
?>
Right now, it seems UnityWebRequest.GetAssetBundledownload asset to RAM and load. Is there anyway to download to hard disk to load?
This is not really complicated. Handle it like a normal file you would download from the internet but save it with the ".unity3d" extension.
1.Download the AssetBundle as a normal file by making a request with UnityWebRequest.
UnityWebRequest www = UnityWebRequest.Get(url);
yield return www.Send();
2.Retrieve the byte array data with DownloadHandler.data then save it to Application.persistentDataPath/yourfolder/filename.unity3d. Make sure that the extension is ".unity3d".
File.WriteAllBytes(handle.data, data);
That's it.
3.To load the data, use AssetBundle.LoadFromFile or AssetBundle.LoadFromFileAsync:
AssetBundleCreateRequest bundle = AssetBundle.LoadFromFileAsync(path);
If still confused, here is what the thing should look like. You may need to make some modification:
Download and Save:
IEnumerator downloadAsset()
{
string url = "http://url.net/YourAsset.unity3d";
UnityWebRequest www = UnityWebRequest.Get(url);
DownloadHandler handle = www.downloadHandler;
//Send Request and wait
yield return www.Send();
if (www.isError)
{
UnityEngine.Debug.Log("Error while Downloading Data: " + www.error);
}
else
{
UnityEngine.Debug.Log("Success");
//handle.data
//Construct path to save it
string dataFileName = "WaterVehicles";
string tempPath = Path.Combine(Application.persistentDataPath, "AssetData");
tempPath = Path.Combine(tempPath, dataFileName + ".unity3d");
//Save
save(handle.data, tempPath);
}
}
void save(byte[] data, string path)
{
//Create the Directory if it does not exist
if (!Directory.Exists(Path.GetDirectoryName(path)))
{
Directory.CreateDirectory(Path.GetDirectoryName(path));
}
try
{
File.WriteAllBytes(path, data);
Debug.Log("Saved Data to: " + path.Replace("/", "\\"));
}
catch (Exception e)
{
Debug.LogWarning("Failed To Save Data to: " + path.Replace("/", "\\"));
Debug.LogWarning("Error: " + e.Message);
}
}
Load:
IEnumerable LoadObject(string path)
{
AssetBundleCreateRequest bundle = AssetBundle.LoadFromFileAsync(path);
yield return bundle;
AssetBundle myLoadedAssetBundle = bundle.assetBundle;
if (myLoadedAssetBundle == null)
{
Debug.Log("Failed to load AssetBundle!");
yield break;
}
AssetBundleRequest request = myLoadedAssetBundle.LoadAssetAsync<GameObject>("boat");
yield return request;
GameObject obj = request.asset as GameObject;
obj.transform.position = new Vector3(0.08f, -2.345f, 297.54f);
obj.transform.Rotate(350.41f, 400f, 20f);
obj.transform.localScale = new Vector3(1.0518f, 0.998f, 1.1793f);
Instantiate(obj);
myLoadedAssetBundle.Unload(false);
}
Right now, it seems UnityWebRequest.GetAssetBundledownload asset to RAM and load. Is there anyway to download to hard disk to load?
This is not really complicated. Handle it like a normal file you would download from the internet but save it with the ".unity3d" extension.
1.Download the AssetBundle as a normal file by making a request with UnityWebRequest.
UnityWebRequest www = UnityWebRequest.Get(url);
yield return www.Send();
2.Retrieve the byte array data with DownloadHandler.data then save it to Application.persistentDataPath/yourfolder/filename.unity3d. Make sure that the extension is ".unity3d".
File.WriteAllBytes(handle.data, data);
That's it.
3.To load the data, use AssetBundle.LoadFromFile or AssetBundle.LoadFromFileAsync:
AssetBundleCreateRequest bundle = AssetBundle.LoadFromFileAsync(path);
If still confused, here is what the thing should look like. You may need to make some modification:
Download and Save:
IEnumerator downloadAsset()
{
string url = "http://url.net/YourAsset.unity3d";
UnityWebRequest www = UnityWebRequest.Get(url);
DownloadHandler handle = www.downloadHandler;
//Send Request and wait
yield return www.Send();
if (www.isError)
{
UnityEngine.Debug.Log("Error while Downloading Data: " + www.error);
}
else
{
UnityEngine.Debug.Log("Success");
//handle.data
//Construct path to save it
string dataFileName = "WaterVehicles";
string tempPath = Path.Combine(Application.persistentDataPath, "AssetData");
tempPath = Path.Combine(tempPath, dataFileName + ".unity3d");
//Save
save(handle.data, tempPath);
}
}
void save(byte[] data, string path)
{
//Create the Directory if it does not exist
if (!Directory.Exists(Path.GetDirectoryName(path)))
{
Directory.CreateDirectory(Path.GetDirectoryName(path));
}
try
{
File.WriteAllBytes(path, data);
Debug.Log("Saved Data to: " + path.Replace("/", "\\"));
}
catch (Exception e)
{
Debug.LogWarning("Failed To Save Data to: " + path.Replace("/", "\\"));
Debug.LogWarning("Error: " + e.Message);
}
}
Load:
IEnumerable LoadObject(string path)
{
AssetBundleCreateRequest bundle = AssetBundle.LoadFromFileAsync(path);
yield return bundle;
AssetBundle myLoadedAssetBundle = bundle.assetBundle;
if (myLoadedAssetBundle == null)
{
Debug.Log("Failed to load AssetBundle!");
yield break;
}
AssetBundleRequest request = myLoadedAssetBundle.LoadAssetAsync<GameObject>("boat");
yield return request;
GameObject obj = request.asset as GameObject;
obj.transform.position = new Vector3(0.08f, -2.345f, 297.54f);
obj.transform.Rotate(350.41f, 400f, 20f);
obj.transform.localScale = new Vector3(1.0518f, 0.998f, 1.1793f);
Instantiate(obj);
myLoadedAssetBundle.Unload(false);
}
So I am using the streaming assets folder to hold some information that I need to run my program. Here is how I am determining the filepath to use:
// Location of the top part of the query
public string fileLocation_queryTop = "/Bro/bro_Headers_TOP.json";
// We are on android
if(Application.platform == RuntimePlatform.Android)
{
assetPath = "jar:file://" + Application.dataPath + "!/assets";
}
// We are on iPhone
else if(Application.platform == RuntimePlatform.IPhonePlayer)
{
assetPath = Application.dataPath + "/Raw";
}
// We are on mac or windows
else
{
assetPath = Application.dataPath + "/StreamingAssets";
}
_query_TOP = File.ReadAllText(assetPath + fileLocation_queryTop);
This works fine on Windows, but I am trying to build for Android today.
Here is error that I am getting
I got the info on what path to use from the Unity docs here
I guess what you are looking for is the Application.streamingAssetsPath. You can find more information about it here.
Since you are on Android, you will have to use the WWW class from Unity to retrieve your files.
EDIT:
Following this link you simply have to adjust the file name and store the result in your _query_TOP variable.
private IEnumerator LoadFile()
{
string filePath = System.IO.Path.Combine(Application.streamingAssetsPath, "file.txt");
if(filePath.Contains("://"))
{
WWW www = new WWW(filePath);
yield return www;
if(string.IsNullOrEmpty(www.error))
{
_query_TOP = www.text;
}
}
else
{
_query_TOP = System.IO.File.ReadAllText(filePath);
}
}