如何从URL下载图片

144

如果URL链接末尾没有图片格式,是否有办法在C#中直接从URL下载图像? URL示例:

https://fbcdn-sphotos-h-a.akamaihd.net/hphotos-ak-xpf1/v/t34.0-12/10555140_10201501435212873_1318258071_n.jpg?oh=97ebc03895b7acee9aebbde7d6b002bf&oe=53C9ABB0&__gda__=1405685729_110e04e71d969d392b63b27ec4f4b24a

当 URL 以图片格式结尾时,我知道如何下载该图片。例如:

http://img1.wikia.nocookie.net/__cb20101219155130/uncyclopedia/images/7/70/Facebooklogin.png
10个回答

189

简单来说,您可以使用以下方法。

using (WebClient client = new WebClient()) 
{
    client.DownloadFile(new Uri(url), @"c:\temp\image35.png");
    // OR 
    client.DownloadFileAsync(new Uri(url), @"c:\temp\image35.png");
}

这些方法几乎与DownloadString(..)和DownloadStringAsync(...)相同。它们将文件存储在目录中,而不是在C#字符串中,并且URI中不需要格式扩展名。

如果您不知道图像的格式(.png、.jpeg等)

public void SaveImage(string imageUrl, string filename, ImageFormat format)
{    
    WebClient client = new WebClient();
    Stream stream = client.OpenRead(imageUrl);
    Bitmap bitmap;  bitmap = new Bitmap(stream);

    if (bitmap != null)
    {
        bitmap.Save(filename, format);
    }
        
    stream.Flush();
    stream.Close();
    client.Dispose();
}

使用方法

try
{
    SaveImage("--- Any Image URL---", "--- Any Image Path ---", ImageFormat.Png)
}
catch(ExternalException)
{
    // Something is wrong with Format -- Maybe required Format is not 
    // applicable here
}
catch(ArgumentNullException)
{   
    // Something wrong with Stream
}

4
@Arsman Ahmad,这是一个完全不同的问题,应该在其他地方寻找或提问。这个帖子是用于下载单个图像的。 - AzNjoE
我认为应该是“public void SaveImage(string imageUrl, string filename, ImageFormat format)”。 - D.JCode
WebClient 已于2022年10月被弃用。 - anon37894203

89

根据您是否知道图像格式,以下是您可以执行的方式:

已知图像格式时将图片下载到文件中

using (WebClient webClient = new WebClient()) 
{
   webClient.DownloadFile("http://yoururl.com/image.png", "image.png") ; 
}

不需要知道图像格式即可将图像下载到文件中

您可以使用Image.FromStream加载任意类型的通用位图(如jpg、png、bmp、gif等),它会自动检测文件类型,您甚至不需要检查url扩展名(这不是一个很好的做法)。示例:

using (WebClient webClient = new WebClient()) 
{
    byte [] data = webClient.DownloadData("https://fbcdn-sphotos-h-a.akamaihd.net/hphotos-ak-xpf1/v/t34.0-12/10555140_10201501435212873_1318258071_n.jpg?oh=97ebc03895b7acee9aebbde7d6b002bf&oe=53C9ABB0&__gda__=1405685729_110e04e71d9");

   using (MemoryStream mem = new MemoryStream(data)) 
   {
       using (var yourImage = Image.FromStream(mem)) 
       { 
          // If you want it as Png
           yourImage.Save("path_to_your_file.png", ImageFormat.Png) ; 

          // If you want it as Jpeg
           yourImage.Save("path_to_your_file.jpg", ImageFormat.Jpeg) ; 
       }
   } 

}

注意:如果下载的内容不是已知的图片类型,则Image.FromStream可能会抛出ArgumentException异常。

请查看MSDN上此参考资料以查找所有可用格式。这里有关于WebClientBitmap的参考信息。


3
需要注意的是,使用Image.FromStream()需要添加"using System.Drawing;"。 - dlchambers
3
请注意,您可以查看响应标头以查看源认为图像使用的格式webClient.ResponseHeaders["Content-Type"],而不是要求图像库检测图像格式。 - bikeman868
这也比将压缩图像扩展为未压缩的位图对象更节省内存,并且可以让您以原始格式保存图像及其原始压缩等。 - bikeman868

52

.NET多年来发生了一些变化,因此本帖中的其他答案已经有些过时:

  • 他们使用System.Drawing中的Image(在.NET Core中不可用)来查找图像格式
  • 他们使用System.Net.WebClient (已弃用)

我们不建议您在新开发中使用WebClient类。 相反,请使用System.Net.Http.HttpClient类。

.NET Core异步解决方案

获取文件扩展名

获取文件扩展名的第一步是从URL中删除所有不必要的部分。我们可以使用Uri.GetLeftPart()和UriPartial.Path,从SchemePath获取所有内容。
换句话说,https://www.example.com/image.png?query&with.dots 变成了 https://www.example.com/image.png

之后,我们可以使用Path.GetExtension()仅获取扩展名(在我的上一个示例中,.png)。

var uriWithoutQuery = uri.GetLeftPart(UriPartial.Path);
var fileExtension = Path.GetExtension(uriWithoutQuery);

下载镜像

接下来应该很简单。使用HttpClient.GetByteArrayAsync下载镜像,创建路径,确保目录存在,然后使用File.WriteAllBytesAsync()将字节写入路径。

private async Task DownloadImageAsync(string directoryPath, string fileName, Uri uri)
{
    using var httpClient = new HttpClient();

    // Get the file extension
    var uriWithoutQuery = uri.GetLeftPart(UriPartial.Path);
    var fileExtension = Path.GetExtension(uriWithoutQuery);

    // Create file path and ensure directory exists
    var path = Path.Combine(directoryPath, $"{fileName}{fileExtension}");
    Directory.CreateDirectory(directoryPath);

    // Download the image and write to the file
    var imageBytes = await httpClient.GetByteArrayAsync(uri);
    await File.WriteAllBytesAsync(path, imageBytes);
}
请注意,您需要以下使用指示符。
using System;
using System.IO;
using System.Threading.Tasks;
using System.Net.Http;

使用示例

var folder = "images";
var fileName = "test";
var url = "https://cdn.discordapp.com/attachments/458291463663386646/592779619212460054/Screenshot_20190624-201411.jpg?query&with.dots";

await DownloadImageAsync(folder, fileName, new Uri(url));

备注

  • 每次调用方法创建新的HttpClient是不好的做法。它应该在整个应用程序中被重复使用。我写了一个简短的示例ImageDownloader(50行),附有更多文档,正确地重用HttpClient并正确处理其释放问题,您可以在这里找到。

你的代码中可能有一个小错别字。在 DownloadImageAsync 中,底部的那一行应该是 await httpClient 而不是 await _httpClient,请检查一下。 - Matt Welke
谢谢@MattWelke,我认为那是从整个类的要点留下来的。将来您应该能够直接编辑帖子并提出建议 :) - MarcusOtter
这次它不让我编辑。有关缺乏声望以提出编辑的问题? - Matt Welke
1
我看过他。 - Zeeshan Ahmad Khalil
2
这应该是2022年的最佳答案。 - anon37894203

37

对于任何想要下载图像但又不想保存到文件中的人:

Image DownloadImage(string fromUrl)
{
    using (System.Net.WebClient webClient = new System.Net.WebClient())
    {
        using (Stream stream = webClient.OpenRead(fromUrl))
        {
            return Image.FromStream(stream);
        }
    }
}

6

.NET Framework允许PictureBox控件从URL加载图像

并在加载完成事件中保存图像

protected void LoadImage() {
 pictureBox1.ImageLocation = "PROXY_URL;}

void pictureBox1_LoadCompleted(object sender, AsyncCompletedEventArgs e) {
   pictureBox1.Image.Save(destination); }

1

大家都为这个问题提供了很好的解决方案,但是所有人提供的解决方案背后存在一个主要问题,即如果图像托管在https上,则不会创建SSL/TLS安全通道。那么我们该怎么办呢?答案很简单,在创建WebClient请求之前添加这两行代码即可。

ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

1
这个方法对我很有效,
我从这里得到了主要代码,
然后使用修复方法,
我能够创建一个可以绕过恼人的403禁止访问错误的方法。
以下是该方法。
    private static void DownloadImage(string url, string saveFilename)
    {
        var httpWebRequest = (HttpWebRequest)WebRequest.Create(url);
        // we need the user agent and default credentials if not,
        //  we get a forbidden request 303 error, which pretty much means the server thinks we are a bot -- which we are.... hehehehehehe
        httpWebRequest.UserAgent = "Case Banana"; // note -- this string can be anything you like, I recommend making it atleast 10 characters
        httpWebRequest.UseDefaultCredentials = true;

        var httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse();
        if ((httpWebResponse.StatusCode != HttpStatusCode.OK &&
            httpWebResponse.StatusCode != HttpStatusCode.Moved &&
            httpWebResponse.StatusCode != HttpStatusCode.Redirect)
            || !httpWebResponse.ContentType.StartsWith("image", StringComparison.OrdinalIgnoreCase))
        {
            return;
        }

        using (var stream = httpWebResponse.GetResponseStream())
        {
            using (var fileStream = File.OpenWrite(saveFilename))
            {
                var bytes = new byte[4096];
                var read = 0;
                do
                {
                    if (stream == null)
                    {
                        continue;
                    }
                    read = stream.Read(bytes, 0, bytes.Length);
                    fileStream.Write(bytes, 0, read);
                } while (read != 0);
            }
        }
    }

1

我发现大多数帖子在第二次迭代后都会超时,特别是当你像我一样循环遍历一堆图片时。因此,为了改进上面的建议,这里提供整个方法:

public System.Drawing.Image DownloadImage(string imageUrl)
    {
        System.Drawing.Image image = null;

        try
        {
            System.Net.HttpWebRequest webRequest = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(imageUrl);
            webRequest.AllowWriteStreamBuffering = true;
            webRequest.Timeout = 30000;
            webRequest.ServicePoint.ConnectionLeaseTimeout = 5000;
            webRequest.ServicePoint.MaxIdleTime = 5000;

            using (System.Net.WebResponse webResponse = webRequest.GetResponse())
            {

                using (System.IO.Stream stream = webResponse.GetResponseStream())
                {
                    image = System.Drawing.Image.FromStream(stream);
                }
            }

            webRequest.ServicePoint.CloseConnectionGroup(webRequest.ConnectionGroupName);
            webRequest = null; 
        }
        catch (Exception ex)
        {
            throw new Exception(ex.Message, ex);

        }


        return image;
    }

0
尝试这个,它对我有效。
将此代码写入您的控制器中。
public class DemoController: Controller

        public async Task<FileStreamResult> GetLogoImage(string logoimage)
        {
            string str = "" ;
            var filePath = Server.MapPath("~/App_Data/" + SubfolderName);//If subfolder exist otherwise leave.
            // DirectoryInfo dir = new DirectoryInfo(filePath);
            string[] filePaths = Directory.GetFiles(@filePath, "*.*");
            foreach (var fileTemp in filePaths)
            {
                  str= fileTemp.ToString();
            }
                return File(new MemoryStream(System.IO.File.ReadAllBytes(str)), System.Web.MimeMapping.GetMimeMapping(str), Path.GetFileName(str));
        }

这是我的视图。
<div><a href="/DemoController/GetLogoImage?Type=Logo" target="_blank">Download Logo</a></div>

-1

私有 void ocmLoadImage_Click(object sender, EventArgs e)

{

字符串 tPathImage;

尝试

{

tPathImage = "https://xxxxx";

WebClient oClient = new WebClient();

Stream oStream = oClient .OpenRead(tPathImage);

Bitmap oBitmap = new Bitmap(oStream );

如果 (oBitmap != null)

{

oBitmap.Save(@"D:\Logo\nick0002.Png", ImageFormat.Png);

}

oStream.Flush();

oStream.Close();

oClient.Dispose();

}

捕获异常(Exception oEx)

{

MessageBox.Show(oEx.Message);

}

}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接