Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Resolved (UnityWebRequest) different files are downloaded with the same information using UnityWebRequest

Discussion in 'Scripting' started by marck_ozz, Dec 12, 2020.

  1. marck_ozz

    marck_ozz

    Joined:
    Nov 30, 2018
    Posts:
    107
    Hello all!!

    I'm moving my code from the old "WWW" API to "UnityWebRequest" and it's resulting a little annoying.

    I'm try to download multiples files, which is already solved using "www" but with "UnityWebRequest" I just can't.

    more specific the files are saved with the same information than the first file downloaded like this:

    If I downloadad:
    pic01.png (105kb)
    pic02.png (500kb)
    text01.txt (25kb)

    I have this in my local path:
    pic01.png (105kb)
    pic02.png (105kb)
    text01.txt (105kb)

    And the pic02 is the same that pic01 and the .txt file has what I thing is the information of pic01. I thougt the "UnityWebRequest.ClearCookieCache();" solves this but not, even try with "www.downloadHandler.Dispose();" but that trigger an error because the consecutive dowload still tries to use that info.

    My code with "www" is the next:

    Code (CSharp):
    1. if (multy)
    2.         {
    3.             _path = "";
    4.             foreach (var p in paths)
    5.             {
    6.                 _path = p;
    7.                 fileName = Path.GetFileName(_path);
    8.                 StartCoroutine(WriteFile(_path, fileName));
    9.                 Debug.Log(_path);
    10.                 result = true;
    11.             }
    12.         }
    13.  
    14.         resultOK = result;
    15.         return;
    16.    
    17.  
    18.     IEnumerator WriteFile(string path, string fileName)
    19.     {
    20.         string ext;
    21.  
    22.         www = new WWW("file://" + path);
    23.         writePath = Path.Combine(Application.persistentDataPath, fileName);
    24.         System.IO.File.WriteAllBytes(writePath, www.bytes);
    25.  
    26.         ext = Path.GetExtension(fileName);
    27.         if(ext == ".txt" || ext == ".png")
    28.         {
    29.             //_pathObj = _path;
    30.             writePathObj = writePath;
    31.         }
    32.  
    33.         yield return new WaitUntil(() => File.Exists(writePath));
    34.     }
    35.  
    36. }
    And this is the code with UnityWebRequest:

    Code (CSharp):
    1.  
    2.  
    3. UnityWebRequest www;
    4.  
    5. if (paths.Length > 0)
    6.         {
    7.             _path = "";
    8.             int i = 0;
    9.             foreach (var p in paths)
    10.             {
    11.                 _path = p;
    12.                 fileName = Path.GetFileName(_path);
    13.                 StartCoroutine(WriteFile(_path, fileName));
    14.                 Debug.Log(_path);
    15.                 i++;
    16.  
    17.                 if (i == paths.Length)
    18.                     lastPath = true;
    19.             }
    20.             return;
    21.         }
    22.     }
    23.  
    24.     IEnumerator WriteFile(string path, string fileName)
    25.     {
    26.         //*****Check if File is valid*****
    27.         if (new FileInfo(path).Length == 0)
    28.         {
    29.             writeFileOK = false;
    30.             Debug.Log("Archivo invalido o vacio");
    31.             yield break;
    32.         }
    33.        
    34.         string ext;
    35.         ext = Path.GetExtension(fileName);
    36.  
    37.         yield return StartCoroutine(GetBytes(path, ext));  
    38.  
    39.         writePath = Path.Combine(Application.persistentDataPath, fileName);
    40.         System.IO.File.WriteAllBytes(writePath, results);
    41.         yield return new WaitUntil(() => File.Exists(writePath));
    42.  
    43.         ext = Path.GetExtension(writePath);
    44.        
    45.         writeFileOK = true;
    46.     }
    47.  
    48.     IEnumerator GetBytes(string path, string ext)
    49.     {
    50.         UnityWebRequest.ClearCookieCache(); // to try to clean old data
    51.         //writesuccess = false;
    52.  
    53.         HashSet<string> set = new HashSet<string>() { ".png", ".jpg", ".jpeg", ".bmp" };
    54.         if (set.Contains(ext))
    55.         {
    56.             www = new UnityWebRequest("file://" + path);
    57.             DownloadHandlerTexture texDl = new DownloadHandlerTexture(true);
    58.             www.downloadHandler = texDl;
    59.            
    60.             www.SendWebRequest();
    61.             while (!www.isDone)
    62.             {
    63.                 Debug.Log(www.downloadProgress);
    64.                 yield return null;
    65.             }
    66.  
    67.             while (!www.downloadHandler.isDone)
    68.             {
    69.                 Debug.Log("Coping File");
    70.                 yield return null;
    71.             }
    72.  
    73.             yield return new WaitUntil(() => www.downloadHandler.isDone);
    74.             Debug.Log("Imagen descargada exitosamente");
    75.  
    76.             if (www.isNetworkError || www.isHttpError)
    77.             {
    78.                 Debug.Log(www.error);
    79.             }
    80.             else
    81.             {              
    82.                 results = www.downloadHandler.data;
    83.                 //www.downloadHandler.Dispose(); //also try with this bus it triggers an error with multiple download
    84.                 resultOK = true;
    85.                 Debug.Log(results.Length);
    86.             }
    87.         }
    88.         else
    89.         {
    90.             www = new UnityWebRequest("file://" + path);
    91.             www.downloadHandler = new DownloadHandlerBuffer();
    92.            
    93.             www.SendWebRequest();
    94.             while (!www.isDone)
    95.             {
    96.                 Debug.Log(www.downloadProgress);
    97.                 yield return null;
    98.             }
    99.  
    100.             Debug.Log(www.isDone);
    101.            
    102.             while (!www.downloadHandler.isDone)
    103.             {
    104.                 Debug.Log("Coping File");
    105.                 yield return null;
    106.             }
    107.  
    108.             yield return new WaitUntil(() => www.downloadHandler.isDone);
    109.             Debug.Log("succsesfull download");
    110.  
    111.             if (www.isNetworkError || www.isHttpError)
    112.             {
    113.                 Debug.Log(www.error);
    114.                 //writesuccess = false;
    115.             }
    116.             else
    117.             {
    118.                 results = www.downloadHandler.data;
    119.                 if (lastPath)
    120.                     resultOK = true;
    121.                 Debug.Log(results.Length);
    122.             }
    123.         }
    124.     }
    Download the files one by one works fine, but that's not what any user would like

    I think that the problem is that in some point I have to delete or clean the "downloadHandler.data" or the "www.data" but I don't have clear how and where. Maybe I'm taking something for granted in the "UnityWebRequest" that "WWW" makes by itself.

    So I'll thanks a lot any help.

    Saludos!!!
     
  2. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    The part where you actually write data into the file seems to be on line 40 of the last code block:
    Code (CSharp):
    1. System.IO.File.WriteAllBytes(writePath, results);
    The data being written is whatever is in the variable "results".

    The declaration for that variable isn't in your posted code. I'm guessing that's a shared variable and all of your download operations are writing their data into the same variable, meaning that the results of the first download will be obliterated the moment the second download finishes.

    Your WriteFile function probably ought to have another parameter that represents the data you want to write into the file (and then restructure the rest of your code as necessary so that the data you want to write actually makes its way to that parameter). Or alternately, whatever class is holding your "results" variable needs to be instantiated separately for each download.
     
    Joe-Censored and marck_ozz like this.
  3. marck_ozz

    marck_ozz

    Joined:
    Nov 30, 2018
    Posts:
    107
    Thanks for the answer.

    yes, I forgot to include the "results" variable, I'm sorry. It is:

    Code (CSharp):
    1. private byte[] results;
    One thing that I have to say first is that seems that the repeated data is the one from the last path not the first one which is more weird.

    It's also weird that every downloaded file has the right name (and therefore, the extension) but wrong data.

    I think that "UnityWebRequest" is slower than "WWW" and that's why I "split" the proccess in two different coroutines and use the "yield return new WaitUntil(for something)" on them to control the downloading & writing on the right order but that's not happening.

    I'll try what you mentioned and let you know what happen.
     
  4. marck_ozz

    marck_ozz

    Joined:
    Nov 30, 2018
    Posts:
    107
    Sorry to double post

    I added this code between lines 37/39 to see if I can use the files length to double check the right data:

    Code (CSharp):
    1. Debug.Log("after Data" + path);
    2.        
    3.         FileInfo info = new FileInfo(path);
    4.         long size = info.Length;
    5.        
    6.         int sizeInt = Convert.ToInt32(size);
    7.         if( sizeInt == results.Length)
    8.         {
    9.             Debug.Log(path + "//" + sizeInt + "// " + "Data size" + results.Length);
    10.         }
    but that's only true with the last file downloaded :confused::confused::confused::confused:. I'm really confused in here, the "after Data" + path" log shows the right number paths every time.
     
  5. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    All of your new test data is 100% exactly what I would have guessed based on the theory I already gave you.
     
    Joe-Censored and marck_ozz like this.
  6. marck_ozz

    marck_ozz

    Joined:
    Nov 30, 2018
    Posts:
    107
    :eek:

    Ok, I'll check all line by line and reorganize my thoughts, I have a lot of notepad with chunk of code here and there haha.
     
  7. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    3,755
    Also, your use of UnityWebRequest is very sloppy and not correct. You should spend some time understanding different download handlers.
    You don't need to use DownloadHandlerTexture if you don't intend to create an actual texture, just need bytes.
    Also, if you want to save downloaded bytes to file, use DownloadHandlerFile (if your Unity version is recent enough to have it).
     
    marck_ozz likes this.
  8. marck_ozz

    marck_ozz

    Joined:
    Nov 30, 2018
    Posts:
    107
    Hello, Thanks for the response.

    I based my code on this "DownloadHandlers" unity doc:
    https://docs.unity3d.com/Manual/UnityWebRequest-CreatingDownloadHandlers.html

    almos of this also:
    https://docs.unity3d.com/ScriptReference/Networking.UnityWebRequest.html

    and take this as an examples:
    https://docs.unity3d.com/ScriptReference/Networking.UnityWebRequest.Get.html
    https://docs.unity3d.com/2019.3/Documentation/Manual/UnityWebRequest-RetrievingTextBinaryData.html

    I do create an actual texture and use it on the app just think that that was not relevant to this topic:

    Code (CSharp):
    1. public RawImage image;
    2.  
    3. image.texture = DownloadHandlerTexture.GetContent(www)
    This works when the user download just one image for specific part of the app in which I don't have problem with.

    I don't use "DownloadHandlerFile" because the unity doc says: "The distinction from other download handlers is that you cannot get data out of this one, all data is saved to a file." And I need to handle and analyse the data in the download process.


    That's why I ask for help haha, I don't have much experience with "UnityWebRequest"

    What am I doing wrong or sloppy?
     
  9. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    3,755
    If your WWW code is actual, it has one valid, but discouraged feature being used - you don't yield return www before reading data. This means you are stalling you app right there until all data is downloaded.

    When you rewrite it using UnityWebRequest, now you properly do yield return, so you probably are running several requests at once. Unfortunately, if you use member variables in your class, then your different coroutines compete for the same variables at the same time leading to funny results. Hence getting the same data from all requests is the consequence of all coroutines eventually using the same variable holding the same request (likely the last one).

    Basically, if you run multiple requests at the same time, move everything into a coroutine, have nothing on a class (or have it in the class in the way that is designed to have more than one thing at the time).
     
    marck_ozz likes this.
  10. marck_ozz

    marck_ozz

    Joined:
    Nov 30, 2018
    Posts:
    107
    That's right, I need to have all the files downloaded before to proceed with the rest of the process and that's what I'm trying to do with UnityWebRequest.

    oh I see!! it is something like @Antistone says :eek:

    I just tryin to do so but withot changing most of the other code but it seems that I don´t have option haha. I'm changing a little here and there and now is a mess. Time to rethink the whole thing.

    Thank again and I'll keep you on date of the result.
     
  11. marck_ozz

    marck_ozz

    Joined:
    Nov 30, 2018
    Posts:
    107
    Thank you guys!!,Now I can download multyples files!!

    Instantieate a "result" variable for every download was the solution (at least for me). Probably is not the most elegant or efficient way but it works. Performance it's not an issue in my case because is for like a visualizer.

    I tryed to clean all my code so anybody can understand it and removeed all the "garbage" not related to the topic that could be confusing, so here it is:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5. using System.IO;
    6. using UnityEngine.Networking;
    7. using System;
    8.  
    9. public class AnotherFileManager : MonoBehaviour
    10. {
    11.     public Text etext;
    12.     public GameObject material_00;
    13.     Renderer renMat_00;
    14.     //UnityWebRequest www;
    15.     string localPath;
    16.     string writePathObj;
    17.  
    18.     public RawImage image;
    19.  
    20.     //Texture txture;
    21.  
    22.     private int type; // Type of request: 1 = Info txt, 2 = imagen, 3 = multyFiles
    23.  
    24.     private string _path;
    25.     //private string _pathObj;
    26.     private string extension;
    27.     private bool resultTxtOK;
    28.     private bool resultImgOK;
    29.     private bool resultMultyOK;
    30.  
    31.     //private byte[] results;
    32.     private bool writeFileOK;
    33.     private bool resultOK;
    34.     private bool lastPath;
    35.  
    36.  
    37.     private void Start()
    38.     {
    39.         //*TODO* Cambiar esto por una funcion que busque al material o materiales del Objeto importado
    40.         renMat_00 = material_00.GetComponent<Renderer>();
    41.         UnityWebRequest.ClearCookieCache();
    42.     }
    43.  
    44.     void Update()
    45.     {
    46.         //*********************Text*********************
    47.         if (resultTxtOK && writeFileOK)
    48.         {
    49.             Debug.Log("Write Text");
    50.             resultTxtOK = false;
    51.             //Your code to handle or use your txt file.
    52.         }
    53.  
    54.         //*********************Imagen*********************
    55.         if (resultImgOK && writeFileOK)
    56.         {
    57.             Debug.Log("Write Imagen");
    58.             resultImgOK = false;
    59.          
    60.             //Your code for handle or usu downloaded image
    61.          
    62.             //In my case (as an example) just this. Apply the image texture on a material
    63.             renMat_00.sharedMaterial.SetTexture("_MainTex", image.texture);                  
    64.         }
    65.  
    66.         //*********************MultyFiles*********************
    67.         if (resultMultyOK && writeFileOK)
    68.         {
    69.             Debug.Log("All files downloaded");
    70.             resultMultyOK = false;
    71.  
    72.             //Your code to handle or use all the files
    73.         }
    74.     }
    75.  
    76.  
    77.     //********************************Call the File downloader********************************
    78.     //********************************Text Importer********************************
    79.  
    80.     public void ShowTxtExplorer() //In my case I call this from a button in the UI
    81.     {
    82.         type = 1;
    83.         WriteResult(YourClassToGetTheDowloadPathOrPaths());// in my case I get String[] whatever paths );
    84.     }
    85.  
    86.     ////********************************Texture Importer********************************
    87.  
    88.     public void ShowImageExplorer() //In my case I call this from a button in the UI
    89.     {
    90.         var extensions = new[]
    91.         {
    92.                 new ExtensionFilter("Image Files", "png", "jpg", "jpeg", "bmp"),
    93.                 //new ExtensionFilter("Sound Files", "mp3", "wav" ),
    94.                 //new ExtensionFilter("All Files", "*" ),
    95.         };
    96.  
    97.         type = 2;
    98.         WriteResult(YourClassToGetTheDowloadPathOrPaths());// in my case I get String[] whatever paths );
    99.     }
    100.  
    101.     ////********************************Multy Importer********************************
    102.  
    103.     public void ShowMultyExplorer() //In my case I call this from a button in the UI
    104.     {
    105.         var extensions = new[]
    106.         {
    107.                 new ExtensionFilter("Select all Files", "txt", "bin", "png", "jpg", "jpeg", "bmp"),
    108.                 //new ExtensionFilter("Sound Files", "mp3", "wav" ),
    109.                 //new ExtensionFilter("All Files", "*" ),
    110.         };
    111.  
    112.         type = 3;
    113.         WriteResult(YourClassToGetTheDowloadPathOrPaths());// in my case I get String[] whatever paths );
    114.     }
    115.  
    116.     public void WriteResult(string[] paths)//, out bool resultOK)
    117.     {
    118.         //Reset the variables
    119.         string fileName;
    120.         writeFileOK = false;
    121.         lastPath = false;
    122.         resultOK = false;
    123.  
    124.         if (paths.Length == 0)
    125.         {
    126.             //resultOK = false;
    127.             Debug.Log("No file Selected"); //in case of cancel the get path class
    128.             _path = "";
    129.             resultOK = false;
    130.             return;
    131.         }
    132.  
    133.         if (paths.Length > 0)
    134.         {
    135.  
    136.             _path = "";
    137.             int i = 0;
    138.             foreach (var p in paths)
    139.             {
    140.                 _path = p;
    141.                 fileName = Path.GetFileName(_path);
    142.                 StartCoroutine(WriteFile(_path, fileName));
    143.                 i++;
    144.  
    145.                 if (i == paths.Length)
    146.                     lastPath = true;
    147.             }
    148.             return;
    149.         }
    150.     }
    151.  
    152.     IEnumerator WriteFile(string path, string fileName)
    153.     {
    154.         //*****Check if File is valid*****
    155.         if (new FileInfo(path).Length == 0)
    156.         {
    157.             writeFileOK = false;
    158.             Debug.Log("Invalid or damage path");
    159.             yield break;
    160.         }
    161.  
    162.         string ext;
    163.         ext = Path.GetExtension(fileName);
    164.  
    165.         yield return StartCoroutine(GetBytes(path, fileName));
    166.      
    167.         yield return new WaitUntil(() => File.Exists(localPath));
    168.      
    169.         ext = Path.GetExtension(localPath);
    170.      
    171.         writeFileOK = true;
    172.     }
    173.  
    174.     IEnumerator GetBytes(string path, string fileName)
    175.     {
    176.         string ext;
    177.         ext = Path.GetExtension(fileName);
    178.         UnityWebRequest.ClearCookieCache();
    179.  
    180.         UnityWebRequest www;
    181.  
    182.         string writePath = Path.Combine(Application.persistentDataPath, fileName);
    183.  
    184.         HashSet<string> set = new HashSet<string>() { ".png", ".jpg", ".jpeg", ".bmp" };
    185.         if (set.Contains(ext))
    186.         {
    187.             www = new UnityWebRequest("file://" + path);
    188.             DownloadHandlerTexture texDl = new DownloadHandlerTexture(true);
    189.             www.downloadHandler = texDl;
    190.  
    191.             www.SendWebRequest();
    192.             while (!www.isDone)
    193.             {
    194.                 Debug.Log(www.downloadProgress);
    195.                 yield return null;
    196.             }
    197.  
    198.             yield return new WaitUntil(() => www.downloadHandler.isDone);
    199.             Debug.Log("Imagen descargada exitosamente");
    200.  
    201.             if (www.isNetworkError || www.isHttpError)
    202.             {
    203.                 Debug.Log(www.error);
    204.             }
    205.             else
    206.             {
    207.                 byte[] results;            
    208.                 results = www.downloadHandler.data;              
    209.                 System.IO.File.WriteAllBytes(writePath, results);
    210.                 resultOK = true;
    211.  
    212.                 if(type == 2)
    213.                     image.texture = texDl.texture;
    214.  
    215.             }
    216.         }
    217.         else
    218.         {
    219.             www = new UnityWebRequest("file://" + path);
    220.             www.downloadHandler = new DownloadHandlerBuffer();
    221.  
    222.             www.SendWebRequest();
    223.             while (!www.isDone)
    224.             {
    225.                 Debug.Log(www.downloadProgress);
    226.                 yield return null;
    227.             }
    228.  
    229.             yield return new WaitUntil(() => www.downloadHandler.isDone);
    230.             Debug.Log("Archivo descargado exitosamente");
    231.  
    232.             if (www.isNetworkError || www.isHttpError)
    233.             {
    234.                 Debug.Log(www.error);
    235.             }
    236.             else
    237.             {
    238.                 byte[] results;
    239.                 results = www.downloadHandler.data;
    240.                 System.IO.File.WriteAllBytes(writePath, results);
    241.              
    242.                 if (lastPath)
    243.                     resultOK = true;
    244.             }
    245.         }
    246.  
    247.         switch (type)
    248.         {
    249.             case 1:
    250.                 if (resultOK)
    251.                 {
    252.                     resultTxtOK = true;
    253.                     localPath = writePath;
    254.                 }
    255.                 break;
    256.             case 2:
    257.                 if (resultOK)
    258.                 {
    259.                     resultImgOK = true;
    260.                     localPath = writePath;
    261.                 }
    262.                 break;
    263.             case 3:
    264.                 if (resultOK && lastPath)
    265.                 {
    266.                     resultMultyOK = true;
    267.                     localPath = writePath;
    268.                 }
    269.                 break;
    270.         }
    271.     }
    272. }
    273.  
    I'm still think that WWW is more friendly to use but maybe because I'm new on UnityWebRequest.

    If you see something wrong or have a doubt please let me know. I hope this thread helps others.

    Saludos.
     
  12. jpgordon00

    jpgordon00

    Joined:
    Jan 9, 2021
    Posts:
    25