C
C#15mo ago
Mekasu0124

✅ Need Help Finishing Up Installer C# Avalonia V11.0.2

https://pastebin.com/gNG6t3Fd This is my current code with all the fluff text to be an example of what I want my installer to do while the loading screen is going, however, I need help with getting the rest of the stuff to happen and making this loading screen become real and put the work in behind it 1. Connecting to the repo 2. Getting the file size of the zip folder 3. Downloading the zip folder into memory 4. Getting the location of the project on the users system without needing user input 5. Extracting the files from the zip folder and over writing the existing files to update the program 6. Correctly and accurately updating the progress bar as each thing happens.
54 Replies
Mekasu0124
Mekasu0124OP15mo ago
my code is commented and I have each step noted as to what I believe should happen at that current step value. I just don't know how to bring it all together and make it all work like it's supposed to. Thanks in advance
JakenVeina
JakenVeina15mo ago
1. Connecting to the repo
what repo? why would your program be connecting to a code repo?
Mekasu0124
Mekasu0124OP15mo ago
did you look at the link containing my code?
JakenVeina
JakenVeina15mo ago
nope
Mekasu0124
Mekasu0124OP15mo ago
everything is commented within my code that follows the bullet points
JakenVeina
JakenVeina15mo ago
yes, you mentioned
Mekasu0124
Mekasu0124OP15mo ago
mhm the link containing my code has been throughouly commented. If anyone can help, that would be great, but I'm not going to re-explain what I've already explained throughout my code to follow the bullet points. Thanks again in advance
arion
arion15mo ago
Are you trying to access the assets part of the api endpoint? Since there's multiple assets on the repo release. So the json path would be $.assets[0].browser_download_url for the Todo_Linux_x64.zip idk if this is best practise but you could download it as a JsonObject, access the ["assets"] and deserialize that as a ReleaseData[] like this
var data = await _client.GetFromJsonAsync<JsonObject>(_url);

var releases = data["assets"].Deserialize<ReleaseData[]>();
var data = await _client.GetFromJsonAsync<JsonObject>(_url);

var releases = data["assets"].Deserialize<ReleaseData[]>();
Then you should be left with a bunch of ReleaseData objects, each containing a link to their respective versions; eg.
.../Todo_Linux_x64.zip
.../Todo_Mac_x64.zip
.../Todo_Windows_x32.zip
.../Todo_Windows_x64.zip
.../Todo_Linux_x64.zip
.../Todo_Mac_x64.zip
.../Todo_Windows_x32.zip
.../Todo_Windows_x64.zip
Additionally, I believe the method Directory.GetCurrentDirectory() uses the working directory instead of the application's location. You could also check if the application is running when the installer is also running, maybe ask or quit the todo app
Mekasu0124
Mekasu0124OP15mo ago
Are you trying to access the assets part of the api endpoint?
correct. I need to have a function that can determine what operating system the user has so that the installer can pull the correct update.
Since there's multiple assets on teh repo release. So the json path would be $.assets[0].browser_download_url for the Todo_Linux_x64.zip
I'm not exactly sure on how to use this since my url is private readonly string _url = "https://api.github.com/repos/mekasu0124/Todo/releases/latest";
idk if this is best practise but you could download it as a JsonObject, access the ["assets"] and deseralize that as a ReleaseData[] like this <code snippet>
where you use <JsonObject> can I make that a class like I did my ReleaseData? If so, how?
Then you should be left with a bunch of ReleaseData objects, each containing a link to their respective versions; eg. <snippet>
This will help a lot with determining the users operating system. How can I go about determining what OS the user has?
Angius
Angius15mo ago
Yes, you can use a class instead of JsonObject. It's even preferable Look at how the API response looks, and create a class that describes it With the properties you want to get Alternatively, you can use something like quicktype.io to just generate classes from JSON, and either remove what you don't need, or leave it as-is
Mekasu0124
Mekasu0124OP15mo ago
ok bet ty
arion
arion15mo ago
Seems everything's been addressed except the last part. You can use RuntimeInformation to determine the OS
arion
arion15mo ago
You could possibly do something like this for the deserialization of what you need:
public class ReleasesResponseDTO
{
[JsonPropertyName("assets")]
public ReleaseData[] Assets { get; set; }
}
public class ReleaseData
{
[JsonPropertyName("browser_download_url")]
public string DownloadUrl { get; set; }

[JsonPropertyName("name")]
public string DownloadName { get; set; }
}
public class ReleasesResponseDTO
{
[JsonPropertyName("assets")]
public ReleaseData[] Assets { get; set; }
}
public class ReleaseData
{
[JsonPropertyName("browser_download_url")]
public string DownloadUrl { get; set; }

[JsonPropertyName("name")]
public string DownloadName { get; set; }
}
then compare file names to OSPlatform i guess
Angius
Angius15mo ago
Oddly enough, there doesn't seem to be a way to just get the OSPlatform
arion
arion15mo ago
I guess for the people implementing their own operating system and feeling left out and wanting to run C# on it
Angius
Angius15mo ago
You can get the OS description string and the platform architecture, so I guess that's something
Mekasu0124
Mekasu0124OP15mo ago
ok so here's what I have so far MainWindowViewModel.cs https://pastebin.com/NjYLDAsQ And then I have /Models/ReleaseData.cs
using System.Text.Json.Serialization;

namespace Installer.Models;

public class ReleaseData
{
[JsonPropertyName("browser_download_url")]
public string DownloadUrl { get; set; }

[JsonPropertyName("size")]
public string DownloadSize { get; set; }
}

public class ReleaseResponseDTO
{
[JsonPropertyName("assets")]
public ReleaseData[] Assets { get; set; }
}
using System.Text.Json.Serialization;

namespace Installer.Models;

public class ReleaseData
{
[JsonPropertyName("browser_download_url")]
public string DownloadUrl { get; set; }

[JsonPropertyName("size")]
public string DownloadSize { get; set; }
}

public class ReleaseResponseDTO
{
[JsonPropertyName("assets")]
public ReleaseData[] Assets { get; set; }
}
Mekasu0124
Mekasu0124OP15mo ago
it doesn't like this part
arion
arion15mo ago
You just use it like
data.Assets
data.Assets
Mekasu0124
Mekasu0124OP15mo ago
ok doing that lead to this
arion
arion15mo ago
you dont need to deserialize it again, its already deserialized in _client.GetFromJsonAsync for you
Mekasu0124
Mekasu0124OP15mo ago
oh ok. my fault. thank you
arion
arion15mo ago
nod so to iterate over the releases you can now do
foreach (var release in data.Assets)
{
}
foreach (var release in data.Assets)
{
}
or alternatively
var releases = data.Assets;
foreach (var release in releases)
{
}
var releases = data.Assets;
foreach (var release in releases)
{
}
Mekasu0124
Mekasu0124OP15mo ago
public async Task GetDownloadUrl()
{
_client.DefaultRequestHeaders.UserAgent.TryParseAdd("request");

try
{
var data = await _client.GetFromJsonAsync<ReleaseResponseDTO>(_url);
var releases = data.Assets;

if (UserOs == "windows")
{
var someItem = releases["name"]["Todo_Windows_x64.zip"];
}
else if (UserOs == "linux")
{
// code here to get download link
}
else if (UserOs == "mac")
{
// code here to get download link
}
else
{
CurrentInformation = "Failed To Obtain Download URL For Given OS.";
return;
}
}
catch (HttpRequestException ex)
{
CurrentInformation = ex.Message;
return;
}
}
public async Task GetDownloadUrl()
{
_client.DefaultRequestHeaders.UserAgent.TryParseAdd("request");

try
{
var data = await _client.GetFromJsonAsync<ReleaseResponseDTO>(_url);
var releases = data.Assets;

if (UserOs == "windows")
{
var someItem = releases["name"]["Todo_Windows_x64.zip"];
}
else if (UserOs == "linux")
{
// code here to get download link
}
else if (UserOs == "mac")
{
// code here to get download link
}
else
{
CurrentInformation = "Failed To Obtain Download URL For Given OS.";
return;
}
}
catch (HttpRequestException ex)
{
CurrentInformation = ex.Message;
return;
}
}
is it better to iterate over the releases? or to use an if/else statement and just index the releases by name and pull the correct name of the correct zip folder? ok so I'm making an adjustment and I want your opinion
public record VersionData(string DownloadUrl, double DownloadSize);
public async Task GetDownloadUrl()
{
_client.DefaultRequestHeaders.UserAgent.TryParseAdd("request");
try
{
var data = await _client.GetFromJsonAsync<ReleaseResponseDTO>(_url);
var releases = data.Assets;
foreach (var release in releases)
{
if (UserOs == "windows" && releases[release]["name"] == "Todo_Windows_x64.zip")
{
string downUrl = data.Assets[release][data.DownloadUrl];
double downSize = data.Assets[release][data.DownloadSize];
return new VersionData(downUrl,downSize);
}
}
}
// /Models/ReleaseData.cs
public class ReleaseData
{
[JsonPropertyName("browser_download_url")]
public string DownloadUrl {get; set;}
[JsonPropertyName("size")]
public double DownloadSize {get; set;}
}
public record VersionData(string DownloadUrl, double DownloadSize);
public async Task GetDownloadUrl()
{
_client.DefaultRequestHeaders.UserAgent.TryParseAdd("request");
try
{
var data = await _client.GetFromJsonAsync<ReleaseResponseDTO>(_url);
var releases = data.Assets;
foreach (var release in releases)
{
if (UserOs == "windows" && releases[release]["name"] == "Todo_Windows_x64.zip")
{
string downUrl = data.Assets[release][data.DownloadUrl];
double downSize = data.Assets[release][data.DownloadSize];
return new VersionData(downUrl,downSize);
}
}
}
// /Models/ReleaseData.cs
public class ReleaseData
{
[JsonPropertyName("browser_download_url")]
public string DownloadUrl {get; set;}
[JsonPropertyName("size")]
public double DownloadSize {get; set;}
}
arion
arion15mo ago
You could do
public enum SupportedOperatingSystem
{
Windows,
Linux,
Mac
}
SupportedOperatingSystem GetSupportedOS()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return SupportedOperatingSystem.Windows;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
return SupportedOperatingSystem.Linux;
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
return SupportedOperatingSystem.Mac;
throw new PlatformNotSupportedException();
}
public enum SupportedOperatingSystem
{
Windows,
Linux,
Mac
}
SupportedOperatingSystem GetSupportedOS()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return SupportedOperatingSystem.Windows;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
return SupportedOperatingSystem.Linux;
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
return SupportedOperatingSystem.Mac;
throw new PlatformNotSupportedException();
}
then in the GetDownloadUrl method you can do something like this
async Task<string> GetDownloadUrl()
{
_client.DefaultRequestHeaders.UserAgent.TryParseAdd("request");

try
{
var data = await _client.GetFromJsonAsync<ReleaseResponseDTO>(_url);
var releases = data.Assets;


var os = GetSupportedOS();
var osReleaseData = releases.FirstOrDefault(x =>
{
var name = x.DownloadName;
return os switch
{
SupportedOperatingSystem.Windows => name.Contains("Windows"),
SupportedOperatingSystem.Linux => name.Contains("Linux"),
SupportedOperatingSystem.Mac => name.Contains("Mac"),
_ => false
};
});

if (osReleaseData == null)
{
CurrentInformation = "Failed to obtain download URL for given OS.";
return string.Empty;
}

}
catch (HttpRequestException ex)
{
CurrentInformation = ex.Message;
return string.Empty;
}
}
async Task<string> GetDownloadUrl()
{
_client.DefaultRequestHeaders.UserAgent.TryParseAdd("request");

try
{
var data = await _client.GetFromJsonAsync<ReleaseResponseDTO>(_url);
var releases = data.Assets;


var os = GetSupportedOS();
var osReleaseData = releases.FirstOrDefault(x =>
{
var name = x.DownloadName;
return os switch
{
SupportedOperatingSystem.Windows => name.Contains("Windows"),
SupportedOperatingSystem.Linux => name.Contains("Linux"),
SupportedOperatingSystem.Mac => name.Contains("Mac"),
_ => false
};
});

if (osReleaseData == null)
{
CurrentInformation = "Failed to obtain download URL for given OS.";
return string.Empty;
}

}
catch (HttpRequestException ex)
{
CurrentInformation = ex.Message;
return string.Empty;
}
}
Though I am generally against returning nulls and string.empty kinda qualifies as that here you could try that, just remember to change your ReleaseData class to include the downloadname
public class ReleaseData
{
[JsonPropertyName("browser_download_url")]
public string DownloadUrl { get; set; }

[JsonPropertyName("size")]
public string DownloadSize { get; set; }

[JsonPropertyName("name")]
public string DownloadName { get; set; }
}
public class ReleaseData
{
[JsonPropertyName("browser_download_url")]
public string DownloadUrl { get; set; }

[JsonPropertyName("size")]
public string DownloadSize { get; set; }

[JsonPropertyName("name")]
public string DownloadName { get; set; }
}
and to make the method return a Task<VersionData> record just uh
string downUrl = data.Assets[release][data.DownloadUrl]; double downSize = data.Assets[release][data.DownloadSize];
remember its not a JsonObject, its a C# class now
Mekasu0124
Mekasu0124OP15mo ago
ok so I took all of that into consideration and made edits to match (because you're smarter than me on this stuff lol) and it's saying GetDownloadUrl() all code paths don't return a value?
Angius
Angius15mo ago
What is returned if there's no exception, and osReleaseData is not null?
arion
arion15mo ago
osReleaseData.DownloadUrl
Mekasu0124
Mekasu0124OP15mo ago
and if I changed async Task<string> GetDownloadUrl() to async Task<VersionData> GetDownloadUrl() would I go do
async Task<VersionData> GetDownloadUrl()
{
_client.DefaultRequestHeaders.UserAgent.TryParseAdd("request");

try
{
var data = await _client.GetFromJsonAsync<ReleaseResponseDTO>(_url);
var releases = data.Assets;
var os = GetSupportedOs();
var osReleaseData = releases.FirstOrDefault(x =>
{
var name = x.DownloadName;
return os switch
{
SupportedOperatingSystem.Windows => name.Contains("Windows"),
SupportedOperatingSystem.Linux => name.Contains("Linux"),
SupportedOperatingSystem.Mac => name.Contains("Mac"),
_ => false
};
});

if (osReleaseData == null)
{
CurrentInformation = "Failed to obtain download URL for given OS.";
return string.Empty;
}
else
{
string downUrl = data.Assets[release][data.DownloadUrl];
double downSize = data.Assets[release][data.DownloadSize];
return new VersionData(downUrl, downSize);
}
catch (HttpRequestException ex)
{
CurrentInformation = ex.Message;
return string.Empty;
}
}
async Task<VersionData> GetDownloadUrl()
{
_client.DefaultRequestHeaders.UserAgent.TryParseAdd("request");

try
{
var data = await _client.GetFromJsonAsync<ReleaseResponseDTO>(_url);
var releases = data.Assets;
var os = GetSupportedOs();
var osReleaseData = releases.FirstOrDefault(x =>
{
var name = x.DownloadName;
return os switch
{
SupportedOperatingSystem.Windows => name.Contains("Windows"),
SupportedOperatingSystem.Linux => name.Contains("Linux"),
SupportedOperatingSystem.Mac => name.Contains("Mac"),
_ => false
};
});

if (osReleaseData == null)
{
CurrentInformation = "Failed to obtain download URL for given OS.";
return string.Empty;
}
else
{
string downUrl = data.Assets[release][data.DownloadUrl];
double downSize = data.Assets[release][data.DownloadSize];
return new VersionData(downUrl, downSize);
}
catch (HttpRequestException ex)
{
CurrentInformation = ex.Message;
return string.Empty;
}
}
I would suppose nothing is being returned if all conditions are true. I noticed that, and that's why I asked is that where I would have the else statement return the new VersionData and change the decorator from Task<string> to Task<VersionData>?
Angius
Angius15mo ago
If that's what you want to return, then sure
Mekasu0124
Mekasu0124OP15mo ago
I feel like I have that wrong should it be
- string downUrl = data.Assets[release][data.DownloadUrl];
+ string downUrl = osReleaseData.DownloadUrl;

- double downSize = data.Assets[release][data.DownloadSize];
+ double downSize = osREleaseData.DownloadSize;
- string downUrl = data.Assets[release][data.DownloadUrl];
+ string downUrl = osReleaseData.DownloadUrl;

- double downSize = data.Assets[release][data.DownloadSize];
+ double downSize = osREleaseData.DownloadSize;
? well I'm returning it as a record so that in the function that actually handles the zip folder can work with the data more easily. Once I've gotten getting the correct download url based off the user's os working correctly, I'll be starting the function that will handle downloading the zip folder to the current working directory, extracting the zip folder and over writing the existing files, then deleting the zip folder from the current working directory, and then showing the button and message telling the user that the update has finished and click ok to relaunch the todo app
arion
arion15mo ago
async Task<VersionData?> GetDownloadUrl()
{
_client.DefaultRequestHeaders.UserAgent.TryParseAdd("request");

try
{
var data = await _client.GetFromJsonAsync<ReleaseResponseDTO>(_url);
var releases = data.Assets;


var os = GetSupportedOS();
var osReleaseData = releases.FirstOrDefault(x =>
{
var name = x.DownloadName;
return os switch
{
SupportedOperatingSystem.Windows => name.Contains("Windows"),
SupportedOperatingSystem.Linux => name.Contains("Linux"),
SupportedOperatingSystem.Mac => name.Contains("Mac"),
_ => false
};
});

if (osReleaseData != null)
return new VersionData(osReleaseData.DownloadUrl, osReleaseData.DownloadSize);

CurrentInformation = "Failed to obtain download URL for given OS.";
return null;

}
catch (HttpRequestException ex)
{
CurrentInformation = ex.Message;
return null;
}
}
async Task<VersionData?> GetDownloadUrl()
{
_client.DefaultRequestHeaders.UserAgent.TryParseAdd("request");

try
{
var data = await _client.GetFromJsonAsync<ReleaseResponseDTO>(_url);
var releases = data.Assets;


var os = GetSupportedOS();
var osReleaseData = releases.FirstOrDefault(x =>
{
var name = x.DownloadName;
return os switch
{
SupportedOperatingSystem.Windows => name.Contains("Windows"),
SupportedOperatingSystem.Linux => name.Contains("Linux"),
SupportedOperatingSystem.Mac => name.Contains("Mac"),
_ => false
};
});

if (osReleaseData != null)
return new VersionData(osReleaseData.DownloadUrl, osReleaseData.DownloadSize);

CurrentInformation = "Failed to obtain download URL for given OS.";
return null;

}
catch (HttpRequestException ex)
{
CurrentInformation = ex.Message;
return null;
}
}
public class ReleaseData
{
[JsonPropertyName("size")]
- public string DownloadSize { get; set; }
+ public ulong DownloadSize { get; set; }
...
}

-public record VersionData(string DownloadUrl, string DownloadSize);
+public record VersionData(string DownloadUrl, ulong DownloadSize);
public class ReleaseData
{
[JsonPropertyName("size")]
- public string DownloadSize { get; set; }
+ public ulong DownloadSize { get; set; }
...
}

-public record VersionData(string DownloadUrl, string DownloadSize);
+public record VersionData(string DownloadUrl, ulong DownloadSize);
Mekasu0124
Mekasu0124OP15mo ago
ok bet. Thank you for that ❤️ I was a little bit off 😛 are there any examples I can view for the remaining things that need to be done? 1. Download, Extract, Over write the existing files in the current working directory (update the application) 2. Have the application remove any files that aren't needed to run the application (Ex: delete the zip folder, ) 3. Have the progress bar correctly and accurately update as this entire process goes along - have progress bar incrementation start off by obtaining the amount of time it will take to download the zip folder, working through the process of updating the application, and removing the no longer needed files
async Task<VersionData?> GetDownloadUrl()
{
_client.DefaultRequestHeaders.UserAgent.TryParseAdd("request");

try
{
var data = await _client.GetFromJsonAsync<ReleaseResponseDTO>(_url);
var releases = data.Assets;
var os = GetSupportedOs();
var osReleaseData = releases.FirstOrDefault(x =>
{
var name = x.DownloadName;
return os switch
{
SupportedOperatingSystem.Windows => name.Contains("Windows"),
SupportedOperatingSystem.Linux => name.Contains("Linux"),
SupportedOperatingSystem.Mac => name.Contains("Mac"),
_ => false
};
});

if (osReleaseData != null)
return new VersionData(
osReleaseData.DownloadName,
osReleaseData.DownloadUrl,
osReleaseData.DownloadSize);

CurrentInformation = "Failed to obtain download URL for given OS.";
return null;
}
catch (HttpRequestException ex)
{
CurrentInformation = ex.Message;
return new VersionData("Error", ex.Message, 0);
}
}

async Task DownloadAndExtract(VersionData versionData)
{
using (WebClient client = new WebClient())
{
Uri url = new Uri(versionData.DownloadUrl);
client.DownloadFileAsync(url, versionData.DownloadName);
}
}
async Task<VersionData?> GetDownloadUrl()
{
_client.DefaultRequestHeaders.UserAgent.TryParseAdd("request");

try
{
var data = await _client.GetFromJsonAsync<ReleaseResponseDTO>(_url);
var releases = data.Assets;
var os = GetSupportedOs();
var osReleaseData = releases.FirstOrDefault(x =>
{
var name = x.DownloadName;
return os switch
{
SupportedOperatingSystem.Windows => name.Contains("Windows"),
SupportedOperatingSystem.Linux => name.Contains("Linux"),
SupportedOperatingSystem.Mac => name.Contains("Mac"),
_ => false
};
});

if (osReleaseData != null)
return new VersionData(
osReleaseData.DownloadName,
osReleaseData.DownloadUrl,
osReleaseData.DownloadSize);

CurrentInformation = "Failed to obtain download URL for given OS.";
return null;
}
catch (HttpRequestException ex)
{
CurrentInformation = ex.Message;
return new VersionData("Error", ex.Message, 0);
}
}

async Task DownloadAndExtract(VersionData versionData)
{
using (WebClient client = new WebClient())
{
Uri url = new Uri(versionData.DownloadUrl);
client.DownloadFileAsync(url, versionData.DownloadName);
}
}
1. How do I download the zip folder to the current working directory so that it stays contained within the projects folder? 2. How do I get the GetDownloadUrl() function to call the DownloadAndExtract() function once the information has been obtained since it returns things?
// for second question
public BeginUpdate()
{
if (GetDownloadUrl() != null)
{
DownloadAndExtract();
}
}
// for second question
public BeginUpdate()
{
if (GetDownloadUrl() != null)
{
DownloadAndExtract();
}
}
would it be like that? The BeginUpdate function is like the brain since it's was is triggered by the button click
Angius
Angius15mo ago
1. Not with WebClient that's for sure. HttpClient has a way to get the byte array or a stream of the file, and then you can save it wherever. 2. Not quite. GetDowloadUrl() needs to be awaited, after all It'd be more like
var data = await GetDowloadUrl();
if (data is {} d)
{
await DownloadAndExtract(d);
}
var data = await GetDowloadUrl();
if (data is {} d)
{
await DownloadAndExtract(d);
}
Mekasu0124
Mekasu0124OP15mo ago
tyvm so how would I do it with HttpClient? because the function DownloadAndExtract() needs to download the zip folder to the current working directory, extract the contents of that folder, and over write the existing files (or delete and replace) so that the todo application can actually update
Angius
Angius15mo ago
var bytes = await _client.GetByteArrayAsync(url);
await File.WriteAllBytesAsync(path, bytes);
var bytes = await _client.GetByteArrayAsync(url);
await File.WriteAllBytesAsync(path, bytes);
url being the, well, URL path beinf where you want to save it
Mekasu0124
Mekasu0124OP15mo ago
ok so with those two lines, it's now downloaded the zip folder. What's the documentation on extracting the contents?
Mekasu0124
Mekasu0124OP15mo ago
Stack Overflow
Unzip files programmatically in .net
I am trying to programatically unzip a zipped file. I have tried using the System.IO.Compression.GZipStream class in .NET, but when my app runs (actually a unit test) I get this exception: Sys...
Angius
Angius15mo ago
Here's the full documentation: https://learn.microsoft.com/en-us/dotnet/standard/io/how-to-compress-and-extract-files ZipFile.ExtractToDirectory(zipPath, extractPath); is all you need, it seems
Mekasu0124
Mekasu0124OP15mo ago
async Task<bool> DownloadAndExtract(VersionData versionData)
{
try
{
// download zip folder to current Directory
var bytes = await _client.GetByteArrayAsync(versionData.DownloadUrl);
await File.WriteAllBytesAsync(Directory.GetCurrentDirectory(), bytes);

// create zip directory path
string zipPath = (Directory.GetCurrentDirectory() + "\\" + versionData.DownloadName);

// create temp folder for zipped files to go to
string tempPath = Path.Combine(Directory.GetCurrentDirectory(), "tempUnzip");

// create extract path
string extractToPath = Directory.GetCurrentDirectory();

// unzip the folder to the temporary path
ZipFile.ExtractToDirectory(zipPath, tempPath);

// build an array of the unzipped files
string[] files = Directory.GetFiles(tempPath);

// iterate over each file
foreach (string file in files)
{
// get file info
FileInfo f = new FileInfo(file);

// if file exists
if (File.Exists(Path.Combine(extractToPath, f.Name)))
{
// delete existing
File.Delete(Path.Combine(extractToPath, f.Name));
// move new in it's place
File.Move(f.FullName, Path.Combine(extractToPath, f.Name));
}
else
{
// otherwise, just move the new file
File.Move(f.FullName, Path.Combine(extractToPath, f.Name));
}
}

// clean up temp files
Directory.Delete(tempPath);
// clean up zip folder itself
Directory.Delete(zipPath);
IsUpdateFinished = true;
return true;
}
catch (Exception ex)
{
CurrentInformation = ex.Message;
return false;
}
}
async Task<bool> DownloadAndExtract(VersionData versionData)
{
try
{
// download zip folder to current Directory
var bytes = await _client.GetByteArrayAsync(versionData.DownloadUrl);
await File.WriteAllBytesAsync(Directory.GetCurrentDirectory(), bytes);

// create zip directory path
string zipPath = (Directory.GetCurrentDirectory() + "\\" + versionData.DownloadName);

// create temp folder for zipped files to go to
string tempPath = Path.Combine(Directory.GetCurrentDirectory(), "tempUnzip");

// create extract path
string extractToPath = Directory.GetCurrentDirectory();

// unzip the folder to the temporary path
ZipFile.ExtractToDirectory(zipPath, tempPath);

// build an array of the unzipped files
string[] files = Directory.GetFiles(tempPath);

// iterate over each file
foreach (string file in files)
{
// get file info
FileInfo f = new FileInfo(file);

// if file exists
if (File.Exists(Path.Combine(extractToPath, f.Name)))
{
// delete existing
File.Delete(Path.Combine(extractToPath, f.Name));
// move new in it's place
File.Move(f.FullName, Path.Combine(extractToPath, f.Name));
}
else
{
// otherwise, just move the new file
File.Move(f.FullName, Path.Combine(extractToPath, f.Name));
}
}

// clean up temp files
Directory.Delete(tempPath);
// clean up zip folder itself
Directory.Delete(zipPath);
IsUpdateFinished = true;
return true;
}
catch (Exception ex)
{
CurrentInformation = ex.Message;
return false;
}
}
what do you think?
Angius
Angius15mo ago
At a glance, LGTM Does it work?
Mekasu0124
Mekasu0124OP15mo ago
I don't know. I'm still lacking the progress bar usage throughout my process. This is my full code for the process https://pastebin.com/mP5zfHFq I have no idea where or how to update the progress bar whatsoever
Pastebin
#region importsusing Installer.Models;using ReactiveUI;using System...
Pastebin.com is the number one paste tool since 2002. Pastebin is a website where you can store text online for a set period of time.
arion
arion15mo ago
looking at this, you might want to include a few try catch finallys just incase you get some Unauthorized exceptions being thrown, then you wouldn't be able to delete the temp path contents
Mekasu0124
Mekasu0124OP15mo ago
async Task<bool> DownloadAndExtract(VersionData versionData)
{
try
{
var bytes = await _client.GetByteArrayAsync(versionData.DownloadUrl);
await File.WriteAllBytesAsync(Directory.GetCurrentDirectory(), bytes);

string zipPath = (Directory.GetCurrentDirectory() + "\\" + versionData.DownloadName);

string tempPath = Path.Combine(Directory.GetCurrentDirectory(), "tempUnzip");

string extractToPath = Directory.GetCurrentDirectory();

ZipFile.ExtractToDirectory(zipPath, tempPath);

string[] files = Directory.GetFiles(tempPath);

foreach (string file in files)
{
FileInfo f = new FileInfo(file);

if (File.Exists(Path.Combine(extractToPath, f.Name)))
{
File.Delete(Path.Combine(extractToPath, f.Name));
File.Move(f.FullName, Path.Combine(extractToPath, f.Name));
}
else
{
File.Move(f.FullName, Path.Combine(extractToPath, f.Name));
}
}

Directory.Delete(tempPath);
Directory.Delete(zipPath);
IsUpdateFinished = true;
return true;
}
catch (Exception ex)
{
CurrentInformation = ex.Message;
return false;
}
}
async Task<bool> DownloadAndExtract(VersionData versionData)
{
try
{
var bytes = await _client.GetByteArrayAsync(versionData.DownloadUrl);
await File.WriteAllBytesAsync(Directory.GetCurrentDirectory(), bytes);

string zipPath = (Directory.GetCurrentDirectory() + "\\" + versionData.DownloadName);

string tempPath = Path.Combine(Directory.GetCurrentDirectory(), "tempUnzip");

string extractToPath = Directory.GetCurrentDirectory();

ZipFile.ExtractToDirectory(zipPath, tempPath);

string[] files = Directory.GetFiles(tempPath);

foreach (string file in files)
{
FileInfo f = new FileInfo(file);

if (File.Exists(Path.Combine(extractToPath, f.Name)))
{
File.Delete(Path.Combine(extractToPath, f.Name));
File.Move(f.FullName, Path.Combine(extractToPath, f.Name));
}
else
{
File.Move(f.FullName, Path.Combine(extractToPath, f.Name));
}
}

Directory.Delete(tempPath);
Directory.Delete(zipPath);
IsUpdateFinished = true;
return true;
}
catch (Exception ex)
{
CurrentInformation = ex.Message;
return false;
}
}
are you talking about in this function?
arion
arion15mo ago
Yes, anything that touches a file can fail Reading, Writing and Deleteing
Mekasu0124
Mekasu0124OP15mo ago
oh ok. This is my first time dealing with files in C# apologies on not being sure of what to do
arion
arion15mo ago
There's multiple things that can fail here, but what you most notably would want is after string tempPath wrap it in a try finally until the end of the foreach loop then include the Directory.Delete logic within the finally block
Mekasu0124
Mekasu0124OP15mo ago
async Task<bool> DownloadAndExtract(VersionData versionData)
{
try
{
var bytes = await _client.GetByteArrayAsync(versionData.DownloadUrl);
await File.WriteAllBytesAsync(Directory.GetCurrentDirectory(), bytes);

string zipPath = (Directory.GetCurrentDirectory() + "\\" + versionData.DownloadName);

string tempPath = Path.Combine(Directory.GetCurrentDirectory(), "tempUnzip");

try
{
string extractToPath = Directory.GetCurrentDirectory();

ZipFile.ExtractToDirectory(zipPath, tempPath);

string[] files = Directory.GetFiles(tempPath);

foreach (string file in files)
{
FileInfo f = new FileInfo(file);

if (File.Exists(Path.Combine(extractToPath, f.Name)))
{
File.Delete(Path.Combine(extractToPath, f.Name));
File.Move(f.FullName, Path.Combine(extractToPath, f.Name));
}
else
{
File.Move(f.FullName, Path.Combine(extractToPath, f.Name));
}
}
}
finally
{
Directory.Delete(tempPath);
Directory.Delete(zipPath);
}

IsUpdateFinished = true;
return true;
}
catch (Exception ex)
{
CurrentInformation = ex.Message;
return false;
}
}
async Task<bool> DownloadAndExtract(VersionData versionData)
{
try
{
var bytes = await _client.GetByteArrayAsync(versionData.DownloadUrl);
await File.WriteAllBytesAsync(Directory.GetCurrentDirectory(), bytes);

string zipPath = (Directory.GetCurrentDirectory() + "\\" + versionData.DownloadName);

string tempPath = Path.Combine(Directory.GetCurrentDirectory(), "tempUnzip");

try
{
string extractToPath = Directory.GetCurrentDirectory();

ZipFile.ExtractToDirectory(zipPath, tempPath);

string[] files = Directory.GetFiles(tempPath);

foreach (string file in files)
{
FileInfo f = new FileInfo(file);

if (File.Exists(Path.Combine(extractToPath, f.Name)))
{
File.Delete(Path.Combine(extractToPath, f.Name));
File.Move(f.FullName, Path.Combine(extractToPath, f.Name));
}
else
{
File.Move(f.FullName, Path.Combine(extractToPath, f.Name));
}
}
}
finally
{
Directory.Delete(tempPath);
Directory.Delete(zipPath);
}

IsUpdateFinished = true;
return true;
}
catch (Exception ex)
{
CurrentInformation = ex.Message;
return false;
}
}
how's this?
arion
arion15mo ago
Should work from the looks of things, im not on pc rn Basically if something errors in that method, files may be left behind that are meant to be temporary. To prevent that we use a finally block as cleanup
Mekasu0124
Mekasu0124OP15mo ago
oh ok. That makes sense thank you 🙂 now the only thing I think I have left to do is figure out where to use my IncrementProgress function so that my progress bar runs parallel with the work the program is doing from start to finish https://pastebin.com/40uKfhQM this is the updated code for anyone who might be able to help with that. I also have to figure out how to launch the files that correspond to linux and mac operating systems as well since they don't use the .exe file
arion
arion15mo ago
Since essentially everything you're using is a stream (from thr get request, to the file saving etc) which means you should be able to implement a progress bar as the file downloads + as you extract it + as you move the contents etc Try searching for something like httpclient progress bar or async stream progress bar
Want results from more Discord servers?
Add your server