Descargar archivo con barra de progreso

I want to download a file in a method, and then continue working with that file using some data that is stored in variables in the first method. I know you can use DownloadFileAsync, but then I need to continue my work in the DownloadFileCompleted method, and the variables can't be reached from there (unless I declare some global ones and use instead, though that isn't the right way I suppose).

So I googled and found another way, by downloading the file manually, bit by bit. That would suit me quite perfect. Though what I want to know is if there are any other methods/solution to my problem that is more simple?

Or if you can play around with the events and achieve something that suits me better :)

Oh, and please change my question if you find a better title of it, I couldn't think of one.

preguntado el 08 de noviembre de 11 a las 16:11

what you mean by "the variables cant be reached from there" ? with DownloadFileCompleted you can reach a global variable perfectly -

@mekici I said that a menos que I declare some global ones. -

ahh ok :) i suggest you to download file async, maybe you can create a class and global List<yourclass> then you can create objects and add that List in DownloadFileCompleted method -

@mekici Thank you for that suggestion :) Though I will use the code by DanH for this thing. -

1 Respuestas

You have to do it piece by piece to update a progress bar. This code does the trick.

public class WebDownloader
    private static readonly ILog log = LogManager.GetLogger(typeof(WebDownloader));

    public delegate void DownloadProgressDelegate(int percProgress);

public static void Download(string uri, string localPath, DownloadProgressDelegate progressDelegate)
        long remoteSize;
        string fullLocalPath; // Full local path including file name if only directory was provided.

        log.InfoFormat("Attempting to download file (Uri={0}, LocalPath={1})", uri, localPath);

            /// Get the name of the remote file.
            Uri remoteUri = new Uri(uri);
            string fileName = Path.GetFileName(remoteUri.LocalPath);

            if (Path.GetFileName(localPath).Length == 0)
                fullLocalPath = Path.Combine(localPath, fileName);
                fullLocalPath = localPath;

            /// Have to get size of remote object through the webrequest as not available on remote files,
            /// although it does work on local files.
            using (WebResponse response = WebRequest.Create(uri).GetResponse())
            using (Stream stream = response.GetResponseStream())
                remoteSize = response.ContentLength;

            log.InfoFormat("Downloading file (Uri={0}, Size={1}, FullLocalPath={2}).",
                uri, remoteSize, fullLocalPath);
        catch (Exception ex)
            throw new ApplicationException(string.Format("Error connecting to URI (Exception={0})", ex.Message), ex);

        int bytesRead = 0, bytesReadTotal = 0;

            using (WebClient client = new WebClient())
            using (Stream streamRemote = client.OpenRead(new Uri(uri)))
            using (Stream streamLocal = new FileStream(fullLocalPath, FileMode.Create, FileAccess.Write, FileShare.None))
                byte[] byteBuffer = new byte[1024 * 1024 * 2]; // 2 meg buffer although in testing only got to 10k max usage.
                int perc = 0;
                while ((bytesRead = streamRemote.Read(byteBuffer, 0, byteBuffer.Length)) > 0)
                    bytesReadTotal += bytesRead;
                    streamLocal.Write(byteBuffer, 0, bytesRead);
                    int newPerc = (int)((double)bytesReadTotal / (double)remoteSize * 100);
                    if (newPerc > perc)
                        log.InfoFormat("...Downloading (BytesRead={0}, Perc={1})...", bytesReadTotal, perc);
                        perc = newPerc;
                        if (progressDelegate != null)
        catch (Exception ex)
            throw new ApplicationException(string.Format("Error downloading file (Exception={0})", ex.Message), ex);

        log.InfoFormat("File successfully downloaded (Uri={0}, BytesDownloaded={1}/{2}, FullLocalPath={3}).",
            uri, bytesReadTotal, remoteSize, fullLocalPath);

You will need to spin off a thread to run this code as its obviously synchronous.


Task.Factory.StartNew(_ => Download(...));

respondido 08 nov., 11:21

+1 for the self explaining code without a needles paragraph with formatted text. - pjvds

Thank you for that great code! I have gone through it and learned some things ;) - fgblomqvist

Glad it is of use. I wrote it quite a while ago, and were I re-writing it now, I might do it differently using Reactive Extensions. The comment about why I'm using a webrequest to get the file size is a bit unclear - I do this because other methods to get file size don't work in various circumstances. - DanH

@DanH what is ILog here? Does it refer to any external library? - Narasappa

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.