# History
[[WebApi] 捣鼓一个资源管理器--数据库辅助服务器文件访问](http://blog.csdn.net/qiujuer/article/details/41744733)
# In This
在做网站开发时,我常常会做一些这样的工作:一张大图中包含了许多的小图,而网站中为了使用其中一张小图我不得不把大图切成一张张的小图。虽然这样的工作不多;但对于我来说在服务器端莫名的存储了一大堆小文件是很反感的事情;或许你会说使用css来完成大图中的小图显示;当然这是可以的。但是我假如需要下载该小图呢?这时无能为力了吧!
所以有了今天的服务器端剪切压缩图片的一章。
# CodeTime
##### 更改
![](https://box.kancloud.cn/2016-01-11_5693565d670e3.jpg)
本次版本相对上一张主要两个地方更改:新增 ImageUtils.cs,修改 ResourceApiController.cs
##### ImageUtils.cs
~~~
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
namespace WebResource.Models
{
public class ImageUtils
{
/// <summary>
/// 图片类型对应的ImageFormat
/// </summary>
/// <param name="type">类型</param>
/// <returns>ImageFormat</returns>
public static ImageFormat GetFormat(string type)
{
Dictionary<string, ImageFormat> types = new Dictionary<string, ImageFormat>();
types.Add("bmp", ImageFormat.Bmp);
types.Add("gif", ImageFormat.Gif);
types.Add("ief", ImageFormat.Jpeg);
types.Add("jpeg", ImageFormat.Jpeg);
types.Add("jpg", ImageFormat.Jpeg);
types.Add("jpe", ImageFormat.Jpeg);
types.Add("png", ImageFormat.Png);
types.Add("tiff", ImageFormat.Tiff);
types.Add("tif", ImageFormat.Tiff);
types.Add("djvu", ImageFormat.Bmp);
types.Add("djv", ImageFormat.Bmp);
types.Add("wbmp", ImageFormat.Bmp);
types.Add("ras", ImageFormat.Bmp);
types.Add("pnm", ImageFormat.Bmp);
types.Add("pbm", ImageFormat.Bmp);
types.Add("pgm", ImageFormat.Bmp);
types.Add("ppm", ImageFormat.Bmp);
types.Add("rgb", ImageFormat.Bmp);
types.Add("xbm", ImageFormat.Bmp);
types.Add("xpm", ImageFormat.Bmp);
types.Add("xwd", ImageFormat.Bmp);
types.Add("ico", ImageFormat.Icon);
types.Add("wmf", ImageFormat.Emf);
types.Add("exif", ImageFormat.Exif);
types.Add("emf", ImageFormat.Emf);
try
{
ImageFormat format = types[type];
if (format != null)
return format;
}
catch { }
return ImageFormat.Bmp;
}
/// <summary>
/// 图片转换为字节
/// </summary>
/// <param name="bitmap">图片</param>
/// <param name="type">类型</param>
/// <returns>字节码</returns>
public static byte[] ToBytes(Bitmap bitmap, string type)
{
using (MemoryStream stream = new MemoryStream())
{
bitmap.Save(stream, GetFormat(type));
byte[] data = new byte[stream.Length];
stream.Seek(0, SeekOrigin.Begin);
stream.Read(data, 0, Convert.ToInt32(stream.Length));
return data;
}
}
/// <summary>
/// 调整图片宽度高度
/// </summary>
/// <param name="bmp">原始Bitmap </param>
/// <param name="newW">新的宽度</param>
/// <param name="newH">新的高度</param>
/// <returns>处理Bitmap</returns>
public static Bitmap Resize(Bitmap bmp, int newW, int newH)
{
try
{
Bitmap b = new Bitmap(newW, newH);
Graphics g = Graphics.FromImage(b);
// 插值算法的质量
g.InterpolationMode = InterpolationMode.Default;
g.DrawImage(bmp, new Rectangle(0, 0, newW, newH), new Rectangle(0, 0, bmp.Width, bmp.Height), GraphicsUnit.Pixel);
g.Dispose();
return b;
}
catch
{
return null;
}
}
/// <summary>
/// 切片Bitmap:指定图片分割为 Row 行,Col 列,取第 N 个图片
/// </summary>
/// <param name="b">等待分割的图片</param>
/// <param name="row">行</param>
/// <param name="col">列</param>
/// <param name="n">取第N个图片</param>
/// <returns>切片后的Bitmap</returns>
public static Bitmap Cut(Bitmap b, int row, int col, int n)
{
int w = b.Width / col;
int h = b.Height / row;
n = n > (col * row) ? col * row : n;
int x = (n - 1) % col;
int y = (n - 1) / col;
x = w * x;
y = h * y;
try
{
Bitmap bmpOut = new Bitmap(w, h, PixelFormat.Format24bppRgb);
Graphics g = Graphics.FromImage(bmpOut);
g.DrawImage(b, new Rectangle(0, 0, w, h), new Rectangle(x, y, w, h), GraphicsUnit.Pixel);
g.Dispose();
return bmpOut;
}
catch
{
return null;
}
}
}
}
~~~
该类的作用就是压缩和剪切图片,其具体实现也都注释清楚了;如果不明白的地方,还望在评论中提出。
然后来看看这次的API更改。
##### ResourceApiController.cs
~~~
/// <summary>
/// Get Image
/// </summary>
/// <param name="name">MD5 Name</param>
/// <returns>File</returns>
[HttpGet]
[Route("{Id}/Img")]
public async Task<HttpResponseMessage> GetImage(string Id, int w = 0, int h = 0, int r = 1, int c = 1, int n = 1)
{
// Return 304
var tag = Request.Headers.IfNoneMatch.FirstOrDefault();
if (Request.Headers.IfModifiedSince.HasValue && tag != null && tag.Tag.Length > 0)
return new HttpResponseMessage(HttpStatusCode.NotModified);
// 判断参数是否正确
if (w < 0 || h < 0 || r < 1 || c < 1 || n < 1)
return new HttpResponseMessage(HttpStatusCode.BadRequest);
// 默认参数情况直接返回
if (w == 0 && h == 0 && r == 1 && c == 1 && n == 1)
return await Get(Id);
// 查找数据库
Resource model = await db.Resources.FindAsync(Id);
// 判断
if (model == null || !GetContentType(model.Type).StartsWith("image"))
return new HttpResponseMessage(HttpStatusCode.BadRequest);
// 加载文件信息
FileInfo info = new FileInfo(Path.Combine(ROOT_PATH, model.Folder, model.Id));
if (!info.Exists)
return new HttpResponseMessage(HttpStatusCode.BadRequest);
// 打开图片
Bitmap bitmap = new Bitmap(info.FullName);
// 剪切
if (r > 1 || c > 1)
{
bitmap = ImageUtils.Cut(bitmap, r, c, n);
}
// 放大缩小
if (w > 0 || h > 0)
{
w = w == 0 ? bitmap.Width : w;
h = h == 0 ? bitmap.Height : h;
bitmap = ImageUtils.Resize(bitmap, w, h);
}
try
{
// Copy To Memory And Close.
byte[] bytes = ImageUtils.ToBytes(bitmap, model.Type);
// Get Tag
string eTag = string.Format("\"{0}\"", HashUtils.GetMD5Hash(bytes));
// 构造返回
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
ContentDispositionHeaderValue disposition = new ContentDispositionHeaderValue(GetDisposition(model.Type));
disposition.FileName = string.Format("{0}.{1}", model.Name, model.Type);
disposition.Name = model.Name;
// 这里写入大小为计算后的大小
disposition.Size = bytes.Length;
result.Content = new ByteArrayContent(bytes);
result.Content.Headers.ContentType = new MediaTypeHeaderValue(GetContentType(model.Type));
result.Content.Headers.ContentDisposition = disposition;
// Set Cache 这个同Get方法
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) };
result.Headers.ETag = new EntityTagHeaderValue(eTag);
return result;
}
catch { }
finally
{
if (bitmap != null)
bitmap.Dispose();
}
return new HttpResponseMessage(HttpStatusCode.BadRequest);
}
~~~
由于代码量较多,我就不全部贴出来了;就把更改的地方写出来了;其中主要就是新增了一个 GetImage 方法。
在方法中,根据传入的参数我们进行一定的判断然后剪切压缩图片;最后打包为 Http 返回。
代码修改的地方就这么多。
# RunTime
##### API
![](https://box.kancloud.cn/2016-01-11_5693565d7b465.jpg)
可以看见多API中多了一个接口;进入看看。
![](https://box.kancloud.cn/2016-01-11_5693565d8fbe1.jpg)
可以看见除了 Id 是必须,其他参数都是有默认值,可以不用传入。
* Id:文件的MD5值,用于接口寻找对应文件
* w:返回的图片宽度
* h:返回的图片高度
* r:row 行,代表横向分割为多少行;默认为1
* c:column 列,代表纵向分割为多少列;默认为1
* n:代表分割后取第N个图片
##### 上传
![](https://box.kancloud.cn/2016-01-11_5693565dac9a4.jpg)
惯例,首先上传一个图片。
##### 访问
![](https://box.kancloud.cn/2016-01-11_5693565dcb7f0.jpg)
访问地址,可以使用原来的也可以使用新的地址,新的地址无非就是在后面加上“/img”
##### 压缩
![](https://box.kancloud.cn/2016-01-11_5693565e07e3e.jpg)
上面是改变宽度和高度后的结果;你可以右键保存图片看看图片是否是被改变了。
##### 剪切
再来看看剪切的效果:
![](https://box.kancloud.cn/2016-01-11_5693565e2a2d3.jpg)
说说原理:
![](https://box.kancloud.cn/2016-01-11_5693565e4431a.jpg)
再来一张:
![](https://box.kancloud.cn/2016-01-11_5693565e6e486.jpg)
这样也就实现了,服务器端一张图片;客户端无数张图片;可以根据需求进行分割压缩;当然复杂一点你还可以加上一些特效;比如模糊;水印等等;这些服务端都可以做。
# END
##### 资源
[[WebApi] 捣鼓一个资源管理器--服务器端分割压缩图片](http://download.csdn.net/detail/qiujuer/8240911)
##### 下一章