# In This
訪問一個網上的圖片資源,我想你應該遇到過這樣的情況:
常規情況下:[http://www.qiujuer.net/Statics/Image/Home/HeadPortrait.png](http://www.qiujuer.net/Statics/Image/Home/HeadPortrait.png)
當然還有這樣:[http://www.qiujuer.net/Resource/75CDF243C30750D397A90E58D412B22E](http://www.qiujuer.net/Resource/75CDF243C30750D397A90E58D412B22E)


可以看到得到同樣的一張圖片卻能采用不一樣的地址,特別是在第二個地址中卻沒有文件的后綴。
這個奇葩了吧?有感覺到很奇怪的么?
##### 其實很簡單
在**第一種情況**中是訪問的當前圖片存儲在網站服務器中的地址。
換言之其在”cdn.duitang.com“網站的服務器中存儲的地址應該是文件夾"uploads/item/201403/04”下的“20140304122431_XMCuj.jpeg”文件。
而輸入第一張圖片中的地址的時候,web服務器將會在其指定文件夾下去搜尋該文件,然后返回圖片。
而**第二種情況**中你能說是在”www.qiujuer.net“網站的“Resource”文件夾下的”75CDF243C30750D397A90E58D412B22E“文件么?
有這樣的可能,但是在這個情況下不是!
其工作原理是訪問當前URL的時候觸發的是“Resource”接口,然后傳入了ID=”75CDF243C30750D397A90E58D412B22E“。
然后WebApi服務器根據該ID通過一定的方式去尋找圖片資源,然后通過資源的方式返回圖片;
而其圖片究竟是存在哪里你并不知道,有可能是在當前網站目錄下的某個文件夾,也可能是其他盤;總之是不固定的。
請問針對第二種情況你能使用爬蟲軟件去爬該圖片么?如果可以;但是我給這個接口加上指定的用戶權限呢?
說了這么多,我們來實現一下!
# CodeTime
首先打開VS-新建項目-web應用程序-名稱“WebResource”-WebApi

進入項目-刪除**ValuesController**
添加一個webapi控制器-**ResourceApiController**

在其中實現Get方法:
~~~
? ? [RoutePrefix("Resource")]
? ? public class ResourceApiController : ApiController
? ? {
? ? ? ? private static readonly long MEMORY_SIZE = 64 * 1024 * 1024;
? ? ? ? private static readonly string ROOT_PATH = HttpContext.Current.Server.MapPath("~/App_Data/");
? ? ? ? [HttpGet]
? ? ? ? [Route("{Id}")]
? ? ? ? public async Task<HttpResponseMessage> Get(string Id)
? ? ? ? {
? ? ? ? ? ? // 進入時判斷當前請求中是否含有 ETag 標識,如果有就返回使用瀏覽器緩存
? ? ? ? ? ? // Return 304
? ? ? ? ? ? var tag = Request.Headers.IfNoneMatch.FirstOrDefault();
? ? ? ? ? ? if (Request.Headers.IfModifiedSince.HasValue && tag != null && tag.Tag.Length > 0)
? ? ? ? ? ? ? ? return new HttpResponseMessage(HttpStatusCode.NotModified);
? ? ? ? ? ? // 進行模擬 App_Data/Image/{id}.png
? ? ? ? ? ? // 打開找到文件
? ? ? ? ? ? FileInfo info = new FileInfo(Path.Combine(ROOT_PATH, "Image", Id + ".png"));
? ? ? ? ? ? if (!info.Exists)
? ? ? ? ? ? ? ? return new HttpResponseMessage(HttpStatusCode.BadRequest);
? ? ? ? ? ? FileStream file = null;
? ? ? ? ? ? try
? ? ? ? ? ? {
? ? ? ? ? ? ? ? // 打開文件
? ? ? ? ? ? ? ? file = new FileStream(info.FullName, FileMode.Open, FileAccess.Read, FileShare.Read);
? ? ? ? ? ? ? ? HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
? ? ? ? ? ? ? ? // 在瀏覽器中顯示 inline
? ? ? ? ? ? ? ? ContentDispositionHeaderValue disposition = new ContentDispositionHeaderValue("inline");
? ? ? ? ? ? ? ? // 寫入文件基本信息
? ? ? ? ? ? ? ? disposition.FileName = file.Name;
? ? ? ? ? ? ? ? disposition.Name = file.Name;
? ? ? ? ? ? ? ? disposition.Size = file.Length;
? ? ? ? ? ? ? ? // 判斷是否大于64Md,如果大于就采用分段流返回,否則直接返回
? ? ? ? ? ? ? ? if (file.Length < MEMORY_SIZE)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? //Copy To Memory And Close.
? ? ? ? ? ? ? ? ? ? byte[] bytes = new byte[file.Length];
? ? ? ? ? ? ? ? ? ? await file.ReadAsync(bytes, 0, (int)file.Length);
? ? ? ? ? ? ? ? ? ? file.Close();
? ? ? ? ? ? ? ? ? ? MemoryStream ms = new MemoryStream(bytes);
? ? ? ? ? ? ? ? ? ? result.Content = new ByteArrayContent(ms.ToArray());
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? else
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? result.Content = new StreamContent(file);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? // 寫入文件類型,這里是圖片png
? ? ? ? ? ? ? ? result.Content.Headers.ContentType = new MediaTypeHeaderValue("image/png");
? ? ? ? ? ? ? ? result.Content.Headers.ContentDisposition = disposition;
? ? ? ? ? ? ? ? // 設置緩存信息,該部分可以沒有,該部分主要是用于與開始部分結合以便瀏覽器使用304緩存
? ? ? ? ? ? ? ? // Set Cache
? ? ? ? ? ? ? ? result.Content.Headers.Expires = new DateTimeOffset(DateTime.Now).AddHours(1);
? ? ? ? ? ? ? ? // 這里應該寫入文件的存儲日期
? ? ? ? ? ? ? ? result.Content.Headers.LastModified = new DateTimeOffset(DateTime.Now);
? ? ? ? ? ? ? ? result.Headers.CacheControl = new CacheControlHeaderValue() { Public = true, MaxAge = TimeSpan.FromHours(1) };
? ? ? ? ? ? ? ? // 設置Etag,這里就簡單采用 Id
? ? ? ? ? ? ? ? result.Headers.ETag = new EntityTagHeaderValue(string.Format("\"{0}\"", Id));
? ? ? ? ? ? ? ? // 返回請求
? ? ? ? ? ? ? ? return result;
? ? ? ? ? ? }
? ? ? ? ? ? catch
? ? ? ? ? ? {
? ? ? ? ? ? ? ? if (file != null)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? file.Close();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? return new HttpResponseMessage(HttpStatusCode.BadRequest);
? ? ? ? }
? ? }
~~~
基本上都進行了注釋,如果有不了解的還請評論中寫下。
# RunTime
### 準備工作
在運行前我們需要在“**App_Data**”下建立文件夾“**Image**”。然后拷貝兩張圖片“**001.png**”\"002.png"進去。
### 運行
點擊運行-瀏覽器中輸入URL:http://localhost:60586/Resource/001

把ID換成002再次輸入:http://localhost:60586/Resource/002

OK,基本的功能我們實現了。
# END Time
### 為什么是“App_Data”文件夾?
在這里有必要說的是“App_Data”在.NET Web 中是受保護的對象。無法直接通過URL訪問;不信?
來試試,輸入URL:http://localhost:60586/App_Data/Image/002.png

所以說,一般數據庫文件是可以放在該文件夾下的;當然現在你可以把圖片放在下面,然后通過接口訪問。
### 是否太簡單了?
的確,在這里第一章只是一個開始;后面會逐漸的進行擴大化。
比如增加:上傳、其他非圖片文件、以及不是001,002等名稱,而是運算一個Id出來、當然還有加上數據的管理等等。
### 代碼
這次的代碼打包了;但是CSDN死活傳不上去,我去啊!不過好在也簡單;就一個方法;下一章中一起打包吧。
### 下一章
下一章已經更新,資源文件也在下一章中;
[[WebApi] 搗鼓一個資源管理器--多文件上傳](http://blog.csdn.net/qiujuer/article/details/41675299)