从URL路径下载文件的简单方法是什么?
使用System.Net.WebClient.DownloadFile:
string remoteUri = "http://www.contoso.com/library/homepage/images/";
string fileName = "ms-banner.gif", myStringWebResource = null;
// Create a new WebClient instance.
using (WebClient myWebClient = new WebClient())
{
myStringWebResource = remoteUri + fileName;
// Download the Web resource and save it into the current filesystem folder.
myWebClient.DownloadFile(myStringWebResource, fileName);
}
using (var client = new WebClient())
{
client.DownloadFile("http://example.com/file/song/a.mpeg", "a.mpeg");
}
using System.Net;
WebClient webClient = new WebClient();
webClient.DownloadFile("http://mysite.com/myfile.txt", @"c:\myfile.txt");
你也可以在WebClient类中使用DownloadFileAsync方法。它将具有指定URI的资源下载到本地文件。此外,此方法不会阻塞调用线程。
示例:
webClient.DownloadFileAsync(new Uri("http://www.example.com/file/test.jpg"), "test.jpg");
欲了解更多信息:
http://csharpexamples.com/download-files-synchronous-asynchronous-url-c/
包括这个命名空间
using System.Net;
异步下载,并在UI线程本身中放置一个ProgressBar来显示下载的状态
private void BtnDownload_Click(object sender, RoutedEventArgs e)
{
using (WebClient wc = new WebClient())
{
wc.DownloadProgressChanged += wc_DownloadProgressChanged;
wc.DownloadFileAsync (
// Param1 = Link of file
new System.Uri("http://www.sayka.com/downloads/front_view.jpg"),
// Param2 = Path to save
"D:\\Images\\front_view.jpg"
);
}
}
// Event to track the progress
void wc_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
progressBar.Value = e.ProgressPercentage;
}
使用GetIsNetworkAvailable()检查网络连接,以避免在未连接到网络时创建空文件。
if (System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable())
{
using (System.Net.WebClient client = new System.Net.WebClient())
{
client.DownloadFileAsync(new Uri("http://www.examplesite.com/test.txt"),
"D:\\test.txt");
}
}
在将状态打印到控制台时下载文件的完整类。
using System;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Threading;
class FileDownloader
{
private readonly string _url;
private readonly string _fullPathWhereToSave;
private bool _result = false;
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(0);
public FileDownloader(string url, string fullPathWhereToSave)
{
if (string.IsNullOrEmpty(url)) throw new ArgumentNullException("url");
if (string.IsNullOrEmpty(fullPathWhereToSave)) throw new ArgumentNullException("fullPathWhereToSave");
this._url = url;
this._fullPathWhereToSave = fullPathWhereToSave;
}
public bool StartDownload(int timeout)
{
try
{
System.IO.Directory.CreateDirectory(Path.GetDirectoryName(_fullPathWhereToSave));
if (File.Exists(_fullPathWhereToSave))
{
File.Delete(_fullPathWhereToSave);
}
using (WebClient client = new WebClient())
{
var ur = new Uri(_url);
// client.Credentials = new NetworkCredential("username", "password");
client.DownloadProgressChanged += WebClientDownloadProgressChanged;
client.DownloadFileCompleted += WebClientDownloadCompleted;
Console.WriteLine(@"Downloading file:");
client.DownloadFileAsync(ur, _fullPathWhereToSave);
_semaphore.Wait(timeout);
return _result && File.Exists(_fullPathWhereToSave);
}
}
catch (Exception e)
{
Console.WriteLine("Was not able to download file!");
Console.Write(e);
return false;
}
finally
{
this._semaphore.Dispose();
}
}
private void WebClientDownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
Console.Write("\r --> {0}%.", e.ProgressPercentage);
}
private void WebClientDownloadCompleted(object sender, AsyncCompletedEventArgs args)
{
_result = !args.Cancelled;
if (!_result)
{
Console.Write(args.Error.ToString());
}
Console.WriteLine(Environment.NewLine + "Download finished!");
_semaphore.Release();
}
public static bool DownloadFile(string url, string fullPathWhereToSave, int timeoutInMilliSec)
{
return new FileDownloader(url, fullPathWhereToSave).StartDownload(timeoutInMilliSec);
}
}
用法:
static void Main(string[] args)
{
var success = FileDownloader.DownloadFile(fileUrl, fullPathWhereToSave, timeoutInMilliSec);
Console.WriteLine("Done - success: " + success);
Console.ReadLine();
}
您可能需要在文件下载期间了解状态并更新ProgressBar,或者在发出请求之前使用凭据。
下面是一个包含这些选项的示例。Lambda符号和字符串插值已被使用:
using System.Net;
// ...
using (WebClient client = new WebClient()) {
Uri ur = new Uri("http://remotehost.do/images/img.jpg");
//client.Credentials = new NetworkCredential("username", "password");
String credentials = Convert.ToBase64String(Encoding.ASCII.GetBytes("Username" + ":" + "MyNewPassword"));
client.Headers[HttpRequestHeader.Authorization] = $"Basic {credentials}";
client.DownloadProgressChanged += (o, e) =>
{
Console.WriteLine($"Download status: {e.ProgressPercentage}%.");
// updating the UI
Dispatcher.Invoke(() => {
progressBar.Value = e.ProgressPercentage;
});
};
client.DownloadDataCompleted += (o, e) =>
{
Console.WriteLine("Download finished!");
};
client.DownloadFileAsync(ur, @"C:\path\newImage.jpg");
}
下面的代码包含下载文件的逻辑与原始名称
private string DownloadFile(string url)
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
string filename = "";
string destinationpath = Environment;
if (!Directory.Exists(destinationpath))
{
Directory.CreateDirectory(destinationpath);
}
using (HttpWebResponse response = (HttpWebResponse)request.GetResponseAsync().Result)
{
string path = response.Headers["Content-Disposition"];
if (string.IsNullOrWhiteSpace(path))
{
var uri = new Uri(url);
filename = Path.GetFileName(uri.LocalPath);
}
else
{
ContentDisposition contentDisposition = new ContentDisposition(path);
filename = contentDisposition.FileName;
}
var responseStream = response.GetResponseStream();
using (var fileStream = File.Create(System.IO.Path.Combine(destinationpath, filename)))
{
responseStream.CopyTo(fileStream);
}
}
return Path.Combine(destinationpath, filename);
}
试试这个:
private void downloadFile(string url)
{
string file = System.IO.Path.GetFileName(url);
WebClient cln = new WebClient();
cln.DownloadFile(url, file);
}
根据我的研究,我发现WebClient。DownloadFileAsync是下载文件的最佳方式。可以在系统中找到。Net命名空间,它也支持。Net core。
下面是下载该文件的示例代码。
using System;
using System.IO;
using System.Net;
using System.ComponentModel;
public class Program
{
public static void Main()
{
new Program().Download("ftp://localhost/test.zip");
}
public void Download(string remoteUri)
{
string FilePath = Directory.GetCurrentDirectory() + "/tepdownload/" + Path.GetFileName(remoteUri); // path where download file to be saved, with filename, here I have taken file name from supplied remote url
using (WebClient client = new WebClient())
{
try
{
if (!Directory.Exists("tepdownload"))
{
Directory.CreateDirectory("tepdownload");
}
Uri uri = new Uri(remoteUri);
//password username of your file server eg. ftp username and password
client.Credentials = new NetworkCredential("username", "password");
//delegate method, which will be called after file download has been complete.
client.DownloadFileCompleted += new AsyncCompletedEventHandler(Extract);
//delegate method for progress notification handler.
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(ProgessChanged);
// uri is the remote url where filed needs to be downloaded, and FilePath is the location where file to be saved
client.DownloadFileAsync(uri, FilePath);
}
catch (Exception)
{
throw;
}
}
}
public void Extract(object sender, AsyncCompletedEventArgs e)
{
Console.WriteLine("File has been downloaded.");
}
public void ProgessChanged(object sender, DownloadProgressChangedEventArgs e)
{
Console.WriteLine($"Download status: {e.ProgressPercentage}%.");
}
}
以上代码文件将下载到项目目录下的tepdownload文件夹中。请阅读代码中的注释以了解以上代码的功能。
如果你需要设置头文件和cookie来下载文件,你需要做一些稍微不同的事情。这里有一个例子……
// Pass in the HTTPGET URL, Full Path w/Filename, and a populated Cookie Container (optional)
private async Task DownloadFileRequiringHeadersAndCookies(string getUrl, string fullPath, CookieContainer cookieContainer, CancellationToken cancellationToken)
{
cookieContainer ??= new CookieContainer(); // TODO: FILL ME AND PASS ME IN
using (var handler = new HttpClientHandler()
{
UseCookies = true,
CookieContainer = cookieContainer, // This will, both, use the cookies passed in, and update/create cookies from the response
ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true, // use only if it gets angry about the SSL endpoints
AllowAutoRedirect = true,
})
{
using (var client = new HttpClient(handler))
{
SetHeaders(client);
using (var response = await client.GetAsync(getUrl, cancellationToken))
{
if (response.IsSuccessStatusCode)
{
var bytes = await response.Content.ReadAsByteArrayAsync(cancellationToken);
await File.WriteAllBytesAsync(fullPath, bytes, cancellationToken); // This overwrites the file
}
else
{
// TODO: HANDLE ME
throw new FileNotFoundException();
}
}
}
}
}
并且,要添加你需要的header…
private void SetHeaders(HttpClient client)
{
// TODO: SET ME
client.DefaultRequestHeaders.Connection.Add("keep-alive");
client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...");
client.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9, ...");
client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip"));
client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("deflate"));
client.DefaultRequestHeaders.AcceptLanguage.Add(new StringWithQualityHeaderValue("en-US"));
client.DefaultRequestHeaders.AcceptLanguage.Add(new StringWithQualityHeaderValue("en", .9));
...
}
旁白:你可以通过以下方式填充cookie容器:
循环遍历前一个响应的cookie。 这个响应可以来自HttpAgilityPack,或者WebClient,或者Puppeteer(有很多选项) 手动输入(来自配置值或硬编码值)。
WebClient已经过时了
如果你想下载到一个文件,通过使用ResponseHeadersRead避免第一次读取到内存,就像这样:
static public async Task HttpDownloadFileAsync(HttpClient httpClient, string url, string fileToWriteTo) {
using HttpResponseMessage response = await httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
using Stream streamToReadFrom = await response.Content.ReadAsStreamAsync();
using Stream streamToWriteTo = File.Open(fileToWriteTo, FileMode.Create);
await streamToReadFrom.CopyToAsync(streamToWriteTo);
}
以上代码更多的是一个大纲,添加正确的错误/异常处理是不平凡的,进度报告也是不平凡的,因为是处置。
我提出了一组c# 9.0扩展类downadfileasync, GetToStringAsync和PostToStringAsync
namespace System.Net.Http {
// HttpResponse is in one of 3 states:
// - ResponseMessageInfo is object && ResponseMessageInfo.IsSuccessStatusCode -> success, inspect ResponseMessageInfo for StatusCode etc
// - ResponseMessageInfo is object && !ResponseMessageInfo.IsSuccessStatusCode -> failure, inspect ResponseMessageInfo for StatusCode, ReasonPhrase etc
// - ResponseMessageInfo is null -> exception, inspect ExceptionInfo fields
public record HttpResponse {
// copies of HttpRequestMessage and HttpResponseMessage which do not have the content and do not need to be disposed
public record HttpRequestMessageInfo(HttpRequestHeaders Headers, HttpMethod Method, HttpRequestOptions Options, Uri? RequestUri, Version Version, HttpVersionPolicy VersionPolicy);
public record HttpResponseMessageInfo(HttpResponseHeaders Headers, bool IsSuccessStatusCode, string? ReasonPhrase, HttpRequestMessageInfo RequestMessage, HttpStatusCode StatusCode, HttpResponseHeaders TrailingHeaders, Version Version);
// holds Http exception information
public record HttpExceptionInfo(HttpRequestMessageInfo HttpRequestMessage, string ErrorMessage, WebExceptionStatus? WebExceptionStatus);
// if ResponseMessageInfo is null ExceptionInfo is not and vice versa
public HttpResponseMessageInfo? ResponseMessageInfo { get; init; }
public HttpExceptionInfo? ExceptionInfo { get; init; }
public HttpResponse(HttpRequestMessage requestMessage, HttpResponseMessage responseMessage) {
var requestMessageInfo = new HttpRequestMessageInfo(requestMessage.Headers, requestMessage.Method, requestMessage.Options, requestMessage.RequestUri, requestMessage.Version, requestMessage.VersionPolicy);
ResponseMessageInfo = new(responseMessage.Headers, responseMessage.IsSuccessStatusCode, responseMessage.ReasonPhrase, requestMessageInfo, responseMessage.StatusCode, responseMessage.TrailingHeaders, responseMessage.Version);
ExceptionInfo = null;
}
public HttpResponse(HttpRequestMessage requestMessage, Exception exception) {
ResponseMessageInfo = null;
var requestMessageInfo = new HttpRequestMessageInfo(requestMessage.Headers, requestMessage.Method, requestMessage.Options, requestMessage.RequestUri, requestMessage.Version, requestMessage.VersionPolicy);
if (exception is WebException ex1 && ex1.Status == WebExceptionStatus.ProtocolError) {
using HttpWebResponse? httpResponse = (HttpWebResponse?)ex1.Response;
ExceptionInfo = new(requestMessageInfo, httpResponse?.StatusDescription ?? "", ex1.Status);
}
else if (exception is WebException ex2) ExceptionInfo = new(requestMessageInfo, ex2.FullMessage(), ex2.Status);
else if (exception is TaskCanceledException ex3 && ex3.InnerException is TimeoutException) ExceptionInfo = new(requestMessageInfo, ex3.InnerException.FullMessage(), WebExceptionStatus.Timeout);
else if (exception is TaskCanceledException ex4) ExceptionInfo = new(requestMessageInfo, ex4.FullMessage(), WebExceptionStatus.RequestCanceled);
else ExceptionInfo = new(requestMessageInfo, exception.FullMessage(), null);
}
public override string ToString() {
if (ResponseMessageInfo is object) {
var msg = ResponseMessageInfo.IsSuccessStatusCode ? "Success" : "Failure";
msg += $" {Enum.GetName(typeof(HttpStatusCode), ResponseMessageInfo.StatusCode)}";
if (ResponseMessageInfo.ReasonPhrase is object) msg += $" {ResponseMessageInfo.ReasonPhrase}";
return msg;
} else if (ExceptionInfo is object) {
var msg = "Failure";
msg += $" {ExceptionInfo.ErrorMessage}";
if (ExceptionInfo.WebExceptionStatus is object) msg += $" {Enum.GetName(typeof(WebExceptionStatus), ExceptionInfo.WebExceptionStatus)}";
return msg;
}
return "NA"; // never reach here
}
}
public static class ExtensionMethods {
// progressCallback recieves (bytesRecieved, percent, speedKbSec) and can return false to cancell download
public static async Task<(bool success, HttpResponse httpResponse)> DownloadFileAsync(this HttpClient httpClient, Uri requestUri, string fileToWriteTo, CancellationTokenSource? cts = null, Func<long, int, float, bool>? progressCallback = null) {
var httpRequestMessage = new HttpRequestMessage { Method = HttpMethod.Get, RequestUri = requestUri };
var created = false;
try {
var cancellationToken = cts?.Token ?? default;
using HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
if (!httpResponseMessage.IsSuccessStatusCode) return (false, new(httpRequestMessage, httpResponseMessage));
var contentLength = httpResponseMessage.Content.Headers.ContentLength;
using Stream streamToReadFrom = await httpResponseMessage.Content.ReadAsStreamAsync();
using Stream streamToWriteTo = File.Open(fileToWriteTo, FileMode.Create);
created = true;
var buffer = new byte[81920];
var bytesRecieved = (long)0;
var stopwatch = Stopwatch.StartNew();
int bytesInBuffer;
while ((bytesInBuffer = await streamToReadFrom.ReadAsync(buffer, cancellationToken)) != 0) {
await streamToWriteTo.WriteAsync(buffer.AsMemory(0, bytesInBuffer), cancellationToken);
bytesRecieved += bytesInBuffer;
if (progressCallback is object) {
var percent = contentLength is object && contentLength != 0 ? (int)Math.Floor(bytesRecieved / (float)contentLength * 100.0) : 0;
var speedKbSec = (float)((bytesRecieved / 1024.0) / (stopwatch.ElapsedMilliseconds / 1000.0));
var proceed = progressCallback(bytesRecieved, percent, speedKbSec);
if (!proceed) {
httpResponseMessage.ReasonPhrase = "Callback cancelled download";
httpResponseMessage.StatusCode = HttpStatusCode.PartialContent;
return (false, new(httpRequestMessage, httpResponseMessage));
}
}
}
return (true, new(httpRequestMessage, httpResponseMessage));
}
catch (Exception ex) {
if (created) try { File.Delete(fileToWriteTo); } catch { };
return (false, new(httpRequestMessage, ex));
}
}
public static async Task<(string? ResponseAsString, HttpResponse httpResponse)> GetToStringAsync(this HttpClient httpClient, Uri requestUri, CancellationTokenSource? cts = null) {
var httpRequestMessage = new HttpRequestMessage { Method = HttpMethod.Get, RequestUri = requestUri };
try {
var cancellationToken = cts?.Token ?? default;
using var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage, cancellationToken);
if (!httpResponseMessage.IsSuccessStatusCode) return (null, new(httpRequestMessage, httpResponseMessage));
var responseAsString = await httpResponseMessage.Content.ReadAsStringAsync();
return (responseAsString, new(httpRequestMessage, httpResponseMessage));
}
catch (Exception ex) {
return (null, new(httpRequestMessage, ex)); ;
}
}
public static async Task<(string? ResponseAsString, HttpResponse httpResponse)> PostToStringAsync(this HttpClient httpClient, Uri requestUri, HttpContent postBuffer, CancellationTokenSource? cts = null) {
var httpRequestMessage = new HttpRequestMessage { Method = HttpMethod.Post, RequestUri = requestUri, Content = postBuffer };
try {
var cancellationToken = cts?.Token ?? default;
using var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage, cancellationToken);
if (!httpResponseMessage.IsSuccessStatusCode) return (null, new(httpRequestMessage, httpResponseMessage));
var responseAsString = await httpResponseMessage.Content.ReadAsStringAsync();
return (responseAsString, new(httpRequestMessage, httpResponseMessage));
}
catch (Exception ex) {
return (null, new(httpRequestMessage, ex));
}
}
}
}
namespace System {
public static class ExtensionMethods {
public static string FullMessage(this Exception ex) {
if (ex is AggregateException aex) return aex.InnerExceptions.Aggregate("[ ", (total, next) => $"{total}[{next.FullMessage()}] ") + "]";
var msg = ex.Message.Replace(", see inner exception.", "").Trim();
var innerMsg = ex.InnerException?.FullMessage();
if (innerMsg is object && innerMsg!=msg) msg = $"{msg} [ {innerMsg} ]";
return msg;
}
}
}
使用方法:
// download to file
var lastPercent = 0;
bool progressCallback(long bytesRecieved, int percent, float speedKbSec) {
if (percent > lastPercent) {
lastPercent = percent;
Log($"Downloading... {percent}% {speedKbSec/1024.0:0.00}Mbps");
}
return true;
}
var (success, httpResponse) = await httpClient.DownloadFileAsync(
new(myUrlString),
localFileName,
null, // CancellationTokenSource
progressCallback
);
if (success) {
// file downloaded to localFile, httpResponse.ResponseMessageInfo contain
// extra information ie headers and status code
} else {
Log(httpResponse.ToString()); // human friendly error information
// if httpResponse.ResponseMessageInfo is object then server refused the request -
// examine httpResponse.ResponseMessageInfo.HttpStatusCode etc
// else we had a Http exception - examine httpResponse.ExceptionInfo
}
// Http get
var (responseAsString, httpResponse) = await httpClient.GetToStringAsync(url);
if (responseAsString is object) {
// responseAsString contains the string response from the server
} else {
// as for DownloadFileAsync
}
// http post
var postBuffer = new StringContent(jsonInString, System.Text.Encoding.UTF8, "application/x-www-form-urlencoded");
var (responseAsString, httpResponse) = await httpClient.PostToStringAsync(url, postBuffer);
if (responseAsString is object) {
// responseAsString contains the string response from the server
} else {
Log(httpResponse.ToString()); // human friendly error informaiton
// as for DownloadFileAsync
}
static void Main(string[] args)
{
DownloadFileAsync().GetAwaiter();
Console.WriteLine("File was downloaded");
Console.Read();
}
private static async Task DownloadFileAsync()
{
WebClient client = new WebClient();
await client.DownloadFileTaskAsync(new Uri("http://somesite.com/myfile.txt"), "mytxtFile.txt");
}
这是我的解决方案,效果很好:
public static void DownloadFile(string url, string pathToSaveFile)
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
// or: ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;
using (WebDownload client = new WebDownload())
{
client.Headers["User-Agent"] = "User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36";
client.DownloadFile(new Uri(url), pathToSaveFile);
}
}
public class WebDownload : WebClient
{
protected override WebRequest GetWebRequest(Uri address)
{
HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(address);
if (request != null)
{
request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
}
return request;
}
}
在.NET Core MVC中,你有时可以简单地这样做:
public async Task<ActionResult> DownloadUrl(string url) {
return Redirect(url);
}
这可能假设您正在尝试下载的MIME类型被浏览器设置为可下载(例如.mp4),因此它不会尝试重定向到网页。
WebRequest, WebClient和ServicePoint在。net 6(源代码- 2021年11月)中已经过时。
使用System.Net.Http.HttpClient类代替:
using (var client = new HttpClient())
{
using (var s = client.GetStreamAsync("https://via.placeholder.com/150"))
{
using (var fs = new FileStream("localfile.jpg", FileMode.OpenOrCreate))
{
s.Result.CopyTo(fs);
}
}
}
相同代码的异步版本:
using var client = new HttpClient();
using var s = await client.GetStreamAsync("https://via.placeholder.com/150");
using var fs = new FileStream("localfile.jpg", FileMode.OpenOrCreate);
await s.CopyToAsync(fs);
有很多答案,但这是我最近使用的。net 6或更高版本的解决方案。
using var httpClient = new HttpClient();
var tempPath = Path.GetTempFileName();
await using var s = await HttpClient.GetStreamAsync(pdfFilePath);
await using var fs = File.OpenWrite(tempPath);
await s.CopyToAsync(fs);
推荐文章
- 如何返回一个文件(FileContentResult)在ASP。净WebAPI
- 如何在使用ASP时更改表名。净身份?
- 在Visual Studio中设置“首选32位”的目的是什么?它实际上是如何工作的?
- 在c#中从URI字符串获取文件名
- 检查SqlDataReader对象中的列名
- 如何将类标记为已弃用?
- c# 8支持。net框架吗?
- Linq-to-Entities Join vs GroupJoin
- 为什么字符串类型的默认值是null而不是空字符串?
- 在list中获取不同值的列表
- 组合框:向项目添加文本和值(无绑定源)
- 使用JavaScript更改URL参数并指定默认值
- URL方案/主机/路径中的“+”是否代表空格?
- 如何为ASP.net/C#应用程序配置文件值中的值添加&号
- 从System.Drawing.Bitmap中加载WPF BitmapImage