20 August, 2025

[C#] Chia sẻ source code phần mềm Image Downloader tải hình ảnh hàng loạt

August 20, 2025 Posted by Tôi Hùng ✅ , , No comments

Chào mọi người.

Đã lâu rồi mình không ghé trang laptrinhvb.net, nay ghé lại thấy anh thảo có chia sẽ bộ mã nguồn download image hoàng loạt cũng khá thú vị nên mình mang về chia sẽ lại cho mọi người.
Như hình, các bạn chỉ cần dán danh sách link vào và bấm nút tải về.

Có thể xem trước (preview) từng hình ảnh.

Dưới đây là video demo ứng dụng:


Full Source Code.


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Linq;

namespace ImageDownloader
{
    public partial class MainForm : Form
    {
        private ListBox listBoxUrls;
        private TextBox textBoxUrls;
        private TextBox textBoxOutputPath;
        private Button buttonBrowseOutput;
        private Button buttonAddUrls;
        private Button buttonRemoveUrl;
        private Button buttonDownloadAll;
        private Button buttonClearAll;
        private ProgressBar progressBar;
        private Label labelStatus;
        private PictureBox pictureBoxPreview;
        private Label labelPreview;
        private Panel panelPreview;
        private static readonly HttpClient httpClient = new HttpClient();

        public MainForm()
        {
            InitializeComponent();
            InitializeHttpClient();
        }

        private void InitializeComponent()
        {
            this.SuspendLayout();

            // Form properties
            this.Text = "Image Downloader - https://laptrinhvb.net";
            this.Size = new Size(1000, 600);
            this.StartPosition = FormStartPosition.CenterScreen;
            this.FormBorderStyle = FormBorderStyle.Sizable;
            this.MinimumSize = new Size(800, 500);

            // URL input textbox (multiline for pasting multiple URLs)
            this.textBoxUrls = new TextBox
            {
                Location = new Point(12, 12),
                Size = new Size(450, 100),
                Multiline = true,
                ScrollBars = ScrollBars.Vertical,
               
            };

            // Add URLs button
            this.buttonAddUrls = new Button
            {
                Location = new Point(470, 12),
                Size = new Size(80, 30),
                Text = "Add URLs",
                UseVisualStyleBackColor = true
            };
            this.buttonAddUrls.Click += ButtonAddUrls_Click;

            // URLs ListBox
            this.listBoxUrls = new ListBox
            {
                Location = new Point(12, 120),
                Size = new Size(550, 200),
                SelectionMode = SelectionMode.MultiExtended
            };
            this.listBoxUrls.SelectedIndexChanged += ListBoxUrls_SelectedIndexChanged;
            this.listBoxUrls.KeyDown += ListBoxUrls_KeyDown;

            // Remove URL button
            this.buttonRemoveUrl = new Button
            {
                Location = new Point(12, 330),
                Size = new Size(100, 30),
                Text = "Remove Selected",
                UseVisualStyleBackColor = true
            };
            this.buttonRemoveUrl.Click += ButtonRemoveUrl_Click;

            // Clear all button
            this.buttonClearAll = new Button
            {
                Location = new Point(120, 330),
                Size = new Size(80, 30),
                Text = "Clear All",
                UseVisualStyleBackColor = true
            };
            this.buttonClearAll.Click += ButtonClearAll_Click;

            // Output path label
            Label labelOutput = new Label
            {
                Location = new Point(12, 375),
                Size = new Size(100, 20),
                Text = "Output Folder:"
            };

            // Output path textbox
            this.textBoxOutputPath = new TextBox
            {
                Location = new Point(12, 400),
                Size = new Size(450, 23),
                Text = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "Downloaded_Images")
            };

            // Browse output button
            this.buttonBrowseOutput = new Button
            {
                Location = new Point(470, 400),
                Size = new Size(75, 23),
                Text = "Browse",
                UseVisualStyleBackColor = true
            };
            this.buttonBrowseOutput.Click += ButtonBrowseOutput_Click;

            // Download all button
            this.buttonDownloadAll = new Button
            {
                Location = new Point(12, 435),
                Size = new Size(120, 35),
                Text = "Download All",
                UseVisualStyleBackColor = true,
                BackColor = Color.LightGreen
            };
            this.buttonDownloadAll.Click += ButtonDownloadAll_Click;

            // Progress bar
            this.progressBar = new ProgressBar
            {
                Location = new Point(12, 485),
                Size = new Size(550, 23),
                Style = ProgressBarStyle.Continuous
            };

            // Status label
            this.labelStatus = new Label
            {
                Location = new Point(12, 520),
                Size = new Size(550, 40),
                Text = "Ready to download images...",
                AutoSize = false
            };

            // Preview Panel
            this.panelPreview = new Panel
            {
                Location = new Point(580, 12),
                Size = new Size(400, 548),
                BorderStyle = BorderStyle.FixedSingle,
                BackColor = Color.WhiteSmoke
            };

            // Preview label
            this.labelPreview = new Label
            {
                Location = new Point(10, 10),
                Size = new Size(380, 20),
                Text = "Click on a URL to preview image",
                TextAlign = ContentAlignment.MiddleCenter,
                Font = new Font("Microsoft Sans Serif", 9, FontStyle.Bold)
            };

            // Picture box for preview
            this.pictureBoxPreview = new PictureBox
            {
                Location = new Point(10, 40),
                Size = new Size(380, 500),
                SizeMode = PictureBoxSizeMode.Zoom,
                BorderStyle = BorderStyle.FixedSingle,
                BackColor = Color.White
            };

            // Add controls to preview panel
            this.panelPreview.Controls.AddRange(new Control[] {
                this.labelPreview,
                this.pictureBoxPreview
            });

            // Add controls to form
            this.Controls.AddRange(new Control[] {
                this.textBoxUrls,
                this.buttonAddUrls,
                this.listBoxUrls,
                this.buttonRemoveUrl,
                this.buttonClearAll,
                labelOutput,
                this.textBoxOutputPath,
                this.buttonBrowseOutput,
                this.buttonDownloadAll,
                this.progressBar,
                this.labelStatus,
                this.panelPreview
            });

            // Handle form resize
            this.Resize += MainForm_Resize;

            this.ResumeLayout();
        }

        private void MainForm_Resize(object sender, EventArgs e)
        {
            // Adjust preview panel position and size when form is resized
            int formWidth = this.ClientSize.Width;
            int formHeight = this.ClientSize.Height;

            // Adjust main controls width
            int mainControlsWidth = Math.Max(300, formWidth - 420);

            textBoxUrls.Width = mainControlsWidth - 120;
            listBoxUrls.Width = mainControlsWidth;
            textBoxOutputPath.Width = mainControlsWidth - 80;
            progressBar.Width = mainControlsWidth;
            labelStatus.Width = mainControlsWidth;

            // Adjust button positions
            buttonAddUrls.Left = textBoxUrls.Right + 10;
            buttonBrowseOutput.Left = textBoxOutputPath.Right + 10;

            // Adjust preview panel
            panelPreview.Left = mainControlsWidth + 20;
            panelPreview.Width = Math.Max(300, formWidth - mainControlsWidth - 40);
            panelPreview.Height = formHeight - 30;

            // Adjust preview controls
            labelPreview.Width = panelPreview.Width - 20;
            pictureBoxPreview.Width = panelPreview.Width - 20;
            pictureBoxPreview.Height = panelPreview.Height - 60;
        }

        private void InitializeHttpClient()
        {
            httpClient.DefaultRequestHeaders.Add("User-Agent",
                "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
            httpClient.Timeout = TimeSpan.FromMinutes(5);
        }

        private void ButtonAddUrls_Click(object sender, EventArgs e)
        {
            string urlsText = textBoxUrls.Text.Trim();
            if (!string.IsNullOrEmpty(urlsText))
            {
                string[] urls = urlsText.Split(new char[] { '\n', '\r' },
                    StringSplitOptions.RemoveEmptyEntries);

                int addedCount = 0;
                int duplicateCount = 0;
                int invalidCount = 0;

                foreach (string url in urls)
                {
                    string trimmedUrl = url.Trim();
                    if (!string.IsNullOrEmpty(trimmedUrl))
                    {
                        if (IsValidImageUrl(trimmedUrl))
                        {
                            if (!listBoxUrls.Items.Contains(trimmedUrl))
                            {
                                listBoxUrls.Items.Add(trimmedUrl);
                                addedCount++;
                            }
                            else
                            {
                                duplicateCount++;
                            }
                        }
                        else
                        {
                            invalidCount++;
                        }
                    }
                }

                textBoxUrls.Clear();

                string statusMessage = $"Added {addedCount} URLs. Total: {listBoxUrls.Items.Count}";
                if (duplicateCount > 0)
                    statusMessage += $" (Skipped {duplicateCount} duplicates)";
                if (invalidCount > 0)
                    statusMessage += $" (Skipped {invalidCount} invalid URLs)";

                labelStatus.Text = statusMessage;

                if (invalidCount > 0)
                {
                    MessageBox.Show($"Skipped {invalidCount} invalid URLs. Only image URLs with extensions (jpg, jpeg, png, gif, bmp, webp) are accepted.",
                        "Invalid URLs", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                }
            }
        }

        private async void ListBoxUrls_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (listBoxUrls.SelectedItem != null)
            {
                string selectedUrl = listBoxUrls.SelectedItem.ToString();
                await LoadPreviewImage(selectedUrl);
            }
            else
            {
                // Clear preview when no selection
                pictureBoxPreview.Image?.Dispose();
                pictureBoxPreview.Image = null;
                labelPreview.Text = "Click on a URL to preview image";
            }
        }

        private async Task LoadPreviewImage(string url)
        {
            try
            {
                labelPreview.Text = "Loading preview...";
                pictureBoxPreview.Image?.Dispose();
                pictureBoxPreview.Image = null;

                // Load image from URL
                byte[] imageBytes = await httpClient.GetByteArrayAsync(url);
                using (var ms = new MemoryStream(imageBytes))
                {
                    Image image = Image.FromStream(ms);
                    pictureBoxPreview.Image = new Bitmap(image); // Create a copy
                }

                // Update label with image info
                if (pictureBoxPreview.Image != null)
                {
                    string fileName = GetFileNameFromUrl(url);
                    labelPreview.Text = $"{fileName} ({pictureBoxPreview.Image.Width}x{pictureBoxPreview.Image.Height})";
                }
            }
            catch (Exception ex)
            {
                pictureBoxPreview.Image?.Dispose();
                pictureBoxPreview.Image = null;
                labelPreview.Text = $"Failed to load preview: {ex.Message}";
            }
        }

        private void ListBoxUrls_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Delete)
            {
                ButtonRemoveUrl_Click(sender, e);
            }
        }

        private void ButtonRemoveUrl_Click(object sender, EventArgs e)
        {
            if (listBoxUrls.SelectedItems.Count > 0)
            {
                var itemsToRemove = new List<object>();
                foreach (object item in listBoxUrls.SelectedItems)
                {
                    itemsToRemove.Add(item);
                }

                foreach (object item in itemsToRemove)
                {
                    listBoxUrls.Items.Remove(item);
                }

                labelStatus.Text = $"Removed {itemsToRemove.Count} URLs. Total: {listBoxUrls.Items.Count}";

                // Clear preview if no items selected
                if (listBoxUrls.SelectedItems.Count == 0)
                {
                    pictureBoxPreview.Image?.Dispose();
                    pictureBoxPreview.Image = null;
                    labelPreview.Text = "Click on a URL to preview image";
                }
            }
        }

        private void ButtonClearAll_Click(object sender, EventArgs e)
        {
            if (listBoxUrls.Items.Count > 0)
            {
                var result = MessageBox.Show("Are you sure you want to clear all URLs?",
                    "Clear All", MessageBoxButtons.YesNo, MessageBoxIcon.Question);

                if (result == DialogResult.Yes)
                {
                    listBoxUrls.Items.Clear();
                    labelStatus.Text = "All URLs cleared.";

                    // Clear preview
                    pictureBoxPreview.Image?.Dispose();
                    pictureBoxPreview.Image = null;
                    labelPreview.Text = "Click on a URL to preview image";
                }
            }
        }

        private void ButtonBrowseOutput_Click(object sender, EventArgs e)
        {
            using (FolderBrowserDialog folderDialog = new FolderBrowserDialog())
            {
                folderDialog.Description = "Select output folder for downloaded images";
                folderDialog.SelectedPath = textBoxOutputPath.Text;

                if (folderDialog.ShowDialog() == DialogResult.OK)
                {
                    textBoxOutputPath.Text = folderDialog.SelectedPath;
                }
            }
        }

        private async void ButtonDownloadAll_Click(object sender, EventArgs e)
        {
            if (listBoxUrls.Items.Count == 0)
            {
                MessageBox.Show("No URLs to download!", "No URLs",
                    MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            string outputPath = textBoxOutputPath.Text.Trim();
            if (string.IsNullOrEmpty(outputPath))
            {
                MessageBox.Show("Please select an output folder!", "No Output Folder",
                    MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            try
            {
                // Create output directory if it doesn't exist
                if (!Directory.Exists(outputPath))
                {
                    Directory.CreateDirectory(outputPath);
                }

                // Disable controls during download
                SetControlsEnabled(false);

                progressBar.Maximum = listBoxUrls.Items.Count;
                progressBar.Value = 0;

                int successCount = 0;
                int failCount = 0;

                for (int i = 0; i < listBoxUrls.Items.Count; i++)
                {
                    string url = listBoxUrls.Items[i].ToString();
                    labelStatus.Text = $"Downloading {i + 1} of {listBoxUrls.Items.Count}: {Path.GetFileName(url)}";

                    try
                    {
                        await DownloadImage(url, outputPath);
                        successCount++;
                    }
                    catch (Exception ex)
                    {
                        failCount++;
                        // Log error but continue with other downloads
                        Console.WriteLine($"Failed to download {url}: {ex.Message}");
                    }

                    progressBar.Value = i + 1;
                    Application.DoEvents(); // Update UI
                }

                labelStatus.Text = $"Download completed! Success: {successCount}, Failed: {failCount}";

                if (successCount > 0)
                {
                    var result = MessageBox.Show($"Download completed!\n\nSuccess: {successCount}\nFailed: {failCount}\n\nOpen output folder?",
                        "Download Complete", MessageBoxButtons.YesNo, MessageBoxIcon.Information);

                    if (result == DialogResult.Yes)
                    {
                        System.Diagnostics.Process.Start("explorer.exe", outputPath);
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Error during download: {ex.Message}", "Download Error",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                labelStatus.Text = "Download failed!";
            }
            finally
            {
                SetControlsEnabled(true);
                progressBar.Value = 0;
            }
        }

        private async Task DownloadImage(string url, string outputPath)
        {
            try
            {
                byte[] imageBytes = await httpClient.GetByteArrayAsync(url);

                // Generate filename from URL
                string fileName = GetFileNameFromUrl(url);
                string fullPath = Path.Combine(outputPath, fileName);

                // Handle duplicate filenames
                int counter = 1;
                string originalFileName = Path.GetFileNameWithoutExtension(fileName);
                string extension = Path.GetExtension(fileName);

                while (File.Exists(fullPath))
                {
                    fileName = $"{originalFileName}_{counter}{extension}";
                    fullPath = Path.Combine(outputPath, fileName);
                    counter++;
                }

                await Task.Run(() => File.WriteAllBytes(fullPath, imageBytes));
            }
            catch (HttpRequestException ex)
            {
                throw new Exception($"Network error: {ex.Message}");
            }
            catch (TaskCanceledException)
            {
                throw new Exception("Download timeout");
            }
            catch (Exception ex)
            {
                throw new Exception($"Download failed: {ex.Message}");
            }
        }

        private string GetFileNameFromUrl(string url)
        {
            try
            {
                Uri uri = new Uri(url);
                string fileName = Path.GetFileName(uri.LocalPath);

                // If no filename in URL, generate one
                if (string.IsNullOrEmpty(fileName) || !fileName.Contains("."))
                {
                    string extension = GetImageExtensionFromUrl(url);
                    fileName = $"image_{DateTime.Now:yyyyMMdd_HHmmss}_{Guid.NewGuid().ToString("N").Substring(0, 8)}{extension}";
                }

                // Remove invalid characters
                string invalidChars = new string(Path.GetInvalidFileNameChars());
                foreach (char c in invalidChars)
                {
                    fileName = fileName.Replace(c, '_');
                }

                return fileName;
            }
            catch
            {
                return $"image_{DateTime.Now:yyyyMMdd_HHmmss}.jpg";
            }
        }

        private string GetImageExtensionFromUrl(string url)
        {
            string[] extensions = { ".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp" };

            foreach (string ext in extensions)
            {
                if (url.ToLower().Contains(ext))
                    return ext;
            }

            return ".jpg"; // Default extension
        }

        private bool IsValidImageUrl(string url)
        {
            if (string.IsNullOrWhiteSpace(url))
                return false;

            try
            {
                Uri uri = new Uri(url);
                if (uri.Scheme != Uri.UriSchemeHttp && uri.Scheme != Uri.UriSchemeHttps)
                    return false;

                string[] validExtensions = { ".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp" };
                string lowerUrl = url.ToLower();

                return validExtensions.Any(ext => lowerUrl.Contains(ext)) ||
                       lowerUrl.Contains("image") ||
                       lowerUrl.Contains("photo") ||
                       lowerUrl.Contains("pic");
            }
            catch
            {
                return false;
            }
        }

        private void SetControlsEnabled(bool enabled)
        {
            buttonAddUrls.Enabled = enabled;
            buttonRemoveUrl.Enabled = enabled;
            buttonClearAll.Enabled = enabled;
            buttonDownloadAll.Enabled = enabled;
            buttonBrowseOutput.Enabled = enabled;
            textBoxUrls.Enabled = enabled;
            listBoxUrls.Enabled = enabled;
        }

        protected override void OnFormClosed(FormClosedEventArgs e)
        {
            // Dispose preview image
            pictureBoxPreview.Image?.Dispose();
            httpClient?.Dispose();
            base.OnFormClosed(e);
        }
    }

    // Program entry point
    
}

Chúc mọi người thành công. Mọi người có thể Download Full Source Code bên dưới để về tìm hiểu và vọc vạch nhé.

Download Source Code


0 Comments:

Post a Comment

Top