Guía de Integración para Clientes C# / .NET

Implementa la descarga de actualizaciones de 250 MB en tus aplicaciones o sistema POS.

Volver al Dashboard

Endpoints API Disponibles

GET
/api/updates/latest
Obtiene JSON con la última versión disponible y hash SHA256.
GET
/api/updates/check?currentVersion=1.0.0
Retorna 200 con detalles si hay versión superior, o 204 si está al día.
GET
/api/updates/download/{fileName}
Descarga el ejecutable con soporte para HTTP Range (descargas reanudables).

Código C# Auto-Updater con Reanudación de Descargas

Servicio en C# con soporte para **HTTP Range (resume)**. Si la conexión de 250 MB se interrumpe, se reanuda automáticamente en el punto exacto.

using System;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Cryptography;
using System.Text.Json;
using System.Threading.Tasks;
using System.Diagnostics;

public class AutoUpdaterService
{
    private readonly HttpClient _httpClient;
    private readonly string _serverUrl;

    public AutoUpdaterService(string serverUrl)
    {
        _serverUrl = serverUrl.TrimEnd('/');
        _httpClient = new HttpClient();
    }

    public async Task CheckAndApplyUpdateAsync(string currentVersion, Action<double, string> onProgress = null)
    {
        string checkUrl = $"{_serverUrl}/api/updates/check?currentVersion={currentVersion}";
        var response = await _httpClient.GetAsync(checkUrl);

        if (response.StatusCode == System.Net.HttpStatusCode.NoContent)
        {
            Console.WriteLine("El sistema ya está actualizado a la última versión.");
            return;
        }

        response.EnsureSuccessStatusCode();
        string json = await response.Content.ReadAsStringAsync();
        using var doc = JsonDocument.Parse(json);
        var root = doc.RootElement;

        string latestVersion = root.GetProperty("version").GetString();
        string downloadUrl = root.GetProperty("downloadUrl").GetString();
        string expectedSha256 = root.GetProperty("sha256Hash").GetString();
        long totalBytes = root.GetProperty("fileSizeBytes").GetInt64();
        string fileName = root.GetProperty("fileName").GetString();

        string tempFilePath = Path.Combine(Path.GetTempPath(), fileName);

        // 1. Soporte para reanudar descarga si ya existían MB descargados
        long existingLength = File.Exists(tempFilePath) ? new FileInfo(tempFilePath).Length : 0;

        using (var request = new HttpRequestMessage(HttpMethod.Get, downloadUrl))
        {
            if (existingLength > 0 && existingLength < totalBytes)
            {
                request.Headers.Range = new RangeHeaderValue(existingLength, null);
                Console.WriteLine($"Reanudando descarga de {existingLength / (1024.0 * 1024.0):F1} MB...");
            }

            using var downloadResponse = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
            downloadResponse.EnsureSuccessStatusCode();

            using var streamToReadFrom = await downloadResponse.Content.ReadAsStreamAsync();
            using var streamToWriteTo = new FileStream(tempFilePath, existingLength > 0 ? FileMode.Append : FileMode.Create, FileAccess.Write, FileShare.None);

            byte[] buffer = new byte[81920];
            long totalRead = existingLength;
            int bytesRead;

            while ((bytesRead = await streamToReadFrom.ReadAsync(buffer, 0, buffer.Length)) > 0)
            {
                await streamToWriteTo.WriteAsync(buffer, 0, bytesRead);
                totalRead += bytesRead;

                if (totalBytes > 0 && onProgress != null)
                {
                    double percent = (double)totalRead / totalBytes * 100.0;
                    string speedText = $"{totalRead / (1024.0 * 1024.0):F1} MB / {totalBytes / (1024.0 * 1024.0):F1} MB";
                    onProgress(percent, speedText);
                }
            }
        }

        // 2. Verificar Hash SHA256
        string computedHash = ComputeSha256(tempFilePath);
        if (!computedHash.Equals(expectedSha256, StringComparison.OrdinalIgnoreCase))
        {
            File.Delete(tempFilePath);
            throw new InvalidDataException("La verificación SHA256 falló. El archivo está dañado.");
        }

        Console.WriteLine("Integridad SHA256 verificada. Ejecutando instalador...");

        // 3. Ejecutar instalador silencioso
        Process.Start(new ProcessStartInfo
        {
            FileName = tempFilePath,
            Arguments = "/VERYSILENT /SUPPRESSMSGBOXES /NORESTART",
            UseShellExecute = true
        });

        Environment.Exit(0);
    }

    private static string ComputeSha256(string filePath)
    {
        using var sha256 = SHA256.Create();
        using var stream = File.OpenRead(filePath);
        byte[] hash = sha256.ComputeHash(stream);
        return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
    }
}