PassPhotoController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using PassPhotoMVC.Helpers;
using System.Collections;
namespace PhotoDemo.Controllers
{
public class PassPhotoController : Controller
{
//初始化辅助类库和局部变量
PhotoUtils myUtils = new PhotoUtils();
//允许上传的最大文件大小
int maxSize = Convert.ToInt32(Settings.AppSettings.maxuploadsize);
// all images are normalized to this height when uploaded
//所有图像在上传时均标准化为此高度
//它们被放大或缩小
int hminRaw = Convert.ToInt32(Settings.AppSettings.uploadheight);
//最终剪裁图像的所需尺寸(w,h)
int hminCropped = Convert.ToInt32(Settings.AppSettings.passheight);
int wminCropped = Convert.ToInt32(Settings.AppSettings.passwidth);
int imgUploadPreview = Convert.ToInt32(Settings.AppSettings.uploadpreviewh);
int prevw = Convert.ToInt32(Settings.AppSettings.previewwidth);
int prevh = Convert.ToInt32(Settings.AppSettings.previewheight);
/// <summary>
/// 确定默认视图的参数
/// </summary>
/// <param name="jsfilepath">只需要覆盖预设值</param>
/// <returns></returns>
public ActionResult Index(String jsFilePath="")
{
String RootPath = Request.Path;
ViewBag.RootPath = RootPath;
ViewBag.Height = prevh.ToString();
ViewBag.Width = prevw.ToString();
ViewBag.PreviewJSMarkup = myUtils.UpdatePreviewJs(prevw, prevh, jsFilePath);
return View();
}
/// <summary>
///如果最终用户尝试使用GET而不是POST调用此操作,则重定向以启动UI的界面
/// </summary>
/// <returns></returns>
[HttpGet]
public ActionResult UploadImage()
{
return RedirectToAction("Index");
}
/// <summary>
/// 处理上传文件并将上传的文件大小调整为标准大小
/// </summary>
/// <param name="mypostedfile"></param>
/// <param name="targetfilename"></param>
/// <param name="targetfolder"></param>
/// <returns></returns>
[HttpPost]
public ActionResult UploadImage(HttpPostedFileBase myPostedFile, String targetFilename, String targetFolder = "raw", String srcimgfolder = "", String jsFilePath = "")
{
// This value is sent back to the view to accomodate relative URLs when in post action
//在POST action时,此值将发回到视图以适应相关URL
String rootPath = Request.Path.Replace("PassPhoto/UploadImage", "");
ViewBag.RootPath = rootPath;
if (myPostedFile != null && myPostedFile.ContentLength > 0)
{
//img文件夹的默认值取自当前服务器上下文,除非在输入参数中另有说明
if (srcimgfolder == "")
{
srcimgfolder = Server.MapPath("~/uploaded_images/" + targetFolder + "/");
}
else
{
if (!srcimgfolder.EndsWith("\\"))
{
srcimgfolder += "\\";
}
srcimgfolder += targetFolder;
}
// preset name of file while testing refactor after tested
//测试后重新测试的文件的预设名称
targetFilename = "image_upload_test.jpg";
ViewBag.Message = myUtils.UploadImage(myPostedFile, targetFilename, srcimgfolder, maxSize, hminRaw);
if (ViewBag.Message == "OK: File uploaded!")
{
//返回修改UI必需的值
ViewBag.ImageName = targetFilename;
ViewBag.ImageUrl = "uploaded_images/" + targetFolder + "/" + targetFilename;
ViewBag.Height = imgUploadPreview.ToString();
ViewBag.Width = myUtils.CalculateResizedWidth(targetFilename, srcimgfolder, imgUploadPreview).ToString();
ViewBag.PreviewDisplay = "normal";
int jsWidth = myUtils.CalculateResizedWidth(targetFilename, srcimgfolder, hminRaw);
ViewBag.PreviewJSMarkup = myUtils.UpdatePreviewJs(jsWidth, hminRaw, jsFilePath);
}
else
{
//返回默认值并显示操作结果的错误信息
ViewBag.ImageName = targetFilename;
ViewBag.Height = prevh.ToString();
ViewBag.Width = prevw.ToString();
}
}
else
{
ViewBag.Message = "错误 - 文件无效 - 上传的文件为空.";
}
return View("Index");
}
/// <summary>
/// 如果最终用户尝试使用GET而不是POST调用此操作,则重定向以启动UI的界面。
/// </summary>
/// <returns></returns>
[HttpGet]
public ActionResult CropImage()
{
return RedirectToAction("Index");
}
/// <summary>
/// 处理剪裁文件,保持纵横比和标准尺寸
/// </summary>
/// <param name="filename"></param>
/// <param name="folder"></param>
/// <param name="X"></param>
/// <param name="Y"></param>
/// <param name="W"></param>
/// <param name="H"></param>
/// <returns></returns>
[HttpPost]
public ActionResult CropImage(String filename, String folder, String X, String Y, String W, String H, String srcimgfolder = "", String jsFilePath = "")
{
String rootPath = Request.Path.Replace("PassPhoto/CropImage", "");
ViewBag.RootPath = rootPath;
String missingpar = "";
//validate required post parameters and return proper error message is something is missing
//验证必需的post参数并返回缺少参数的合适的错误消息
if (filename == null )
{
missingpar = "filename";
}
if (folder == null)
{
if (missingpar != "")
{
missingpar += ", ";
}
missingpar = "folder";
}
if (missingpar.Length >0)
{
String ermsg1 = "Error - missing parameter";
if (missingpar.Contains(","))
{
ermsg1 += "s";
}
ViewBag.Message = ermsg1 + ": " + missingpar;
}
else
{
// 将post的值转换为int
int W1 = Convert.ToInt32(W);
int H1 = Convert.ToInt32(H);
int X1 = Convert.ToInt32(X);
int Y1 = Convert.ToInt32(Y);
//srcimagefolder的默认值将在运行时从服务器上下文确定
if (srcimgfolder == "")
{
srcimgfolder = Server.MapPath("~/uploaded_images/");
}
else
{
if (!srcimgfolder.EndsWith("\\"))
{
srcimgfolder += "\\";
}
}
String targetfolder = "cropped";
ViewBag.Message = myUtils.CropImage(filename, folder, targetfolder, srcimgfolder, X1, Y1, W1, H1);
if (ViewBag.Message == "OK - 文件裁剪完成了")
{
ViewBag.Height = imgUploadPreview.ToString();
ViewBag.Width = myUtils.CalculateResizedWidth(filename, srcimgfolder + targetfolder, imgUploadPreview).ToString();
ViewBag.ImageName = filename;
ViewBag.PreviewDisplay = "normal";
ViewBag.ImageUrl = "uploaded_images/cropped/" + filename;
ViewBag.PreviewJSMarkup = myUtils.UpdatePreviewJs(wminCropped, hminCropped, jsFilePath);
}
}
return View("Index");
}
}
}
PhotoUtils.cs
using System;
using System.Web;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
namespace PhotoDemo.Helpers
{
/// <summary>
/// 处理上传图像的功能(调整大小/裁剪)
/// </summary>
///
public class PhotoUtils
{
/// <summary>
/// Handle uploading of images, calls resizing for creating standard size images
/// based on AppSettings parameters
/// 处理图像的上传,根据AppSettings参数调用,调整大小以创建标准尺寸图像
/// </summary>
/// <param name="postedfile"></param>
/// <param name="targetfilename"></param>
/// <param name="folder"></param>
/// <param name="maxsize"></param>
/// <param name="hmindimension"></param>
/// <returns></returns>
public String UploadImage(HttpPostedFileBase postedfile, String targetfilename, String folder, int maxsize = 262144, int hmindimension = 0)
{
String result = "";
// 如果hmindimension=0,则使用默认设置
if (hmindimension == 0)
{
hmindimension = Convert.ToInt32(Settings.AppSettings.uploadheight);
}
Boolean fileOK = false;
int fileSize = postedfile.ContentLength;
String maxk = ((int)((double)maxsize / 1024)).ToString();
if (fileSize > 0 & targetfilename.Length > 0)
{
String fileExtension = System.IO.Path.GetExtension(postedfile.FileName).ToLower();
String[] allowedExtensions = { ".jpg", ".jpeg" };
for (int i = 0; i <= allowedExtensions.Length - 1; i++)
{
if (allowedExtensions[i].Equals(fileExtension))
{
fileOK = true;
}
}
if (fileOK)
{
if (fileSize < maxsize)
{
try
{
result = ResizeImageUpload(postedfile.InputStream, targetfilename, folder, hmindimension);
}
catch(Exception ex)
{
result = "错误:无法上传文件<br>" + ex.Message;
}
}
else
{
result = "错误:文件大于 " + maxk + "K.请上传较小的图像";
}
}
else
{
result = "错误:无法接受此类型的文件.";
}
}
else
{
result = "错误:无法上传没有有效的目标文件名的照片.";
}
return result;
}
/// <summary>
/// 处理上传图像的大小,使所有图像都缩放到标准高度
/// </summary>
/// <param name="inputfilestream"></param>
/// <param name="finalfilename"></param>
/// <param name="folderpath"></param>
/// <param name="hmindimension"></param>
/// <returns></returns>
public String ResizeImageUpload(Stream inputfilestream, String finalfilename, String folderpath, int hmindimension)
{
String result = "";
// 强制最终上传的图像以具有固定的高度
int newStillWidth, newStillHeight;
int ori1;
Image originalimg;
try
{
originalimg = System.Drawing.Image.FromStream(inputfilestream);
if (originalimg.Width > originalimg.Height)
{
//景观图片规则
ori1 = originalimg.Height;
newStillHeight = hmindimension;
newStillWidth = (int)((double)originalimg.Width * hmindimension / ori1);
}
else
{
//肖像图片规则
ori1 = originalimg.Width;
newStillHeight = hmindimension;
newStillWidth = (int)((double)newStillHeight * originalimg.Width / originalimg.Height);
}
Bitmap still = new Bitmap(newStillWidth, newStillHeight);
Graphics gr_dest_still = Graphics.FromImage(still);
SolidBrush sb = new SolidBrush(System.Drawing.Color.White);
gr_dest_still.FillRectangle(sb, 0, 0, still.Width, still.Height);
gr_dest_still.DrawImage(originalimg, 0, 0, still.Width, still.Height);
try
{
ImageCodecInfo codecencoder = GetEncoder("image/jpeg");
int quality = 90;
EncoderParameters encodeparams = new EncoderParameters(1);
EncoderParameter qualityparam = new EncoderParameter(Encoder.Quality, quality);
encodeparams.Param[0] = qualityparam;
still.SetResolution(96, 96);
if (!folderpath.EndsWith("\\")){
folderpath += "\\";
}
still.Save(folderpath + finalfilename, codecencoder, encodeparams);
result = "OK: File uploaded!";
}
catch(Exception ex)
{
result = "ERROR: 保存图像时出现问题. " + ex.Message;
}
if (still!=null)
{
still.Dispose();
}
}
catch(Exception ex)
{
result = "ERROR: 这不是我们可以处理的图片. " + ex.Message;
}
return result;
}
/// <summary>
/// 处理裁剪图像,将裁剪的图像缩放到AppSettings文件中配置的标准尺寸
/// </summary>
/// <param name="filename"></param>
/// <param name="sourcefolder"></param>
/// <param name="targetfolder"></param>
/// <param name="imgfolder"></param>
/// <param name="X"></param>
/// <param name="Y"></param>
/// <param name="W"></param>
/// <param name="H"></param>
/// <returns></returns>
public String CropImage(String filename, String sourcefolder, String targetfolder, String imgfolder, int X, int Y, int W, int H)
{
String result = "";
// 强制最终裁剪的图像以具有固定的尺寸
int croppedfinalw, croppedfinalh;
croppedfinalh = Convert.ToInt32(Settings.AppSettings.passheight);
croppedfinalw = Convert.ToInt32(Settings.AppSettings.passwidth);
try
{
if (!imgfolder.EndsWith("\\"))
{
imgfolder += "\\";
}
String sourcepath = imgfolder + sourcefolder + "\\";
Bitmap image1 = (Bitmap)Image.FromFile(sourcepath + filename, true);
Rectangle rect = new Rectangle(X, Y, W, H);
Bitmap cropped = image1.Clone(rect, image1.PixelFormat);
// 释放原始图像,以防我们需要在下面覆盖它
if (image1 != null)
{
image1.Dispose();
}
Bitmap finalcropped= new Bitmap(croppedfinalw, croppedfinalh);
Graphics gr_finalcropped = Graphics.FromImage(finalcropped);
SolidBrush sb = new SolidBrush(System.Drawing.Color.White);
gr_finalcropped.FillRectangle(sb, 0, 0, finalcropped.Width, finalcropped.Height);
gr_finalcropped.DrawImage(cropped, 0, 0, finalcropped.Width, finalcropped.Height);
try
{
ImageCodecInfo codecencoder = GetEncoder("image/jpeg");
int quality = 92;
EncoderParameters encodeparams = new EncoderParameters(1);
EncoderParameter qualityparam = new EncoderParameter(Encoder.Quality, quality);
encodeparams.Param[0] = qualityparam;
finalcropped.SetResolution(240, 240);
sourcepath = sourcepath.Replace(sourcefolder, targetfolder);
finalcropped.Save(sourcepath + filename, codecencoder, encodeparams);
result = "OK - 文件裁剪完成了";
}
catch(Exception ex)
{
result = "ERROR: 保存图像时出现问题。 " + ex.Message;
}
if (cropped != null)
{
cropped.Dispose();
}
if (finalcropped != null)
{
finalcropped.Dispose();
}
}
catch(Exception ex)
{
result = "ERROR: 这不是我们可以处理的图片. " + ex.Message;
}
return result;
}
public ImageCodecInfo GetEncoder(String mimetype)
{
ImageCodecInfo result = null;
foreach (ImageCodecInfo codec in ImageCodecInfo.GetImageEncoders())
{
if (codec.MimeType == mimetype){
result = codec;
}
}
return result;
}
public int CalculateResizedWidth(String filename, String folderpath, int newh)
{
int result = 0;
if (!folderpath.EndsWith("\\"))
{
folderpath += "\\";
}
String fullpath = folderpath + filename;
Bitmap image1 = (Bitmap)Image.FromFile(fullpath, true);
if (image1 != null)
{
result = (int)((double)newh * image1.Width / image1.Height);
image1.Dispose();
}
return result;
}
/// <summary>
/// Updates the javascript code sent to the view to handle the cropping
/// based on the dimensions of the source image used for cropping.
/// 更新发送到视图的JavaScript代码,以根据用于裁剪的源图像的尺寸来处理裁剪。
/// </summary>
/// <param name="neww"></param>
/// <param name="newh"></param>
/// <param name="filepath"></param>
/// <returns></returns>
public String UpdatePreviewJs(int neww, int newh, String filepath="")
{
String result = "";
//读取默认的js源文件
//该结构允许通过在单元测试中传递测试文件路径进行单元测试
if (filepath == "" || filepath==null)
{
HttpContext ctx = HttpContext.Current;
if (ctx != null)
{
filepath = ctx.Server.MapPath("~/js/tiffjcroppreset.js");
}
}
result = System.IO.File.ReadAllText(filepath);
int startselect = result.IndexOf("width: Math.round");
int startvalue = result.IndexOf("(", startselect);
int endvalue = result.IndexOf(")", startvalue);
String selectvalue = result.Substring(startvalue, endvalue - startvalue + 1);
String newvalue = "(rx*" + neww.ToString() + ")";
result = result.Replace(selectvalue, newvalue);
//根据源图像改变高度
startselect = result.IndexOf("height: Math.round");
startvalue = result.IndexOf("(", startselect);
endvalue = result.IndexOf(")", startvalue);
selectvalue = result.Substring(startvalue, endvalue - startvalue + 1);
newvalue = "(ry*" + newh.ToString() + ")";
result = result.Replace(selectvalue, newvalue);
//配置前置宽高比
//从设置确定固定图像比例
String ratio = Settings.AppSettings.passwidth + "/" + Settings.AppSettings.passheight;
startselect = result.IndexOf("aspectRatio:");
startvalue = result.IndexOf(" ", startselect);
endvalue = result.IndexOf(",", startvalue);
selectvalue = result.Substring(startvalue, endvalue - startvalue + 1);
newvalue = " " + ratio + ",";
result = result.Replace(selectvalue, newvalue);
//配置rx的默认值
startselect = result.IndexOf("var rx");
startvalue = result.IndexOf("=", startselect);
endvalue = result.IndexOf(";", startvalue);
selectvalue = result.Substring(startvalue, endvalue - startvalue + 1);
newvalue = "= " + Settings.AppSettings.previewwidth + " / c.w;";
result = result.Replace(selectvalue, newvalue);
// 配置ry的默认值
startselect = result.IndexOf("var ry ");
startvalue = result.IndexOf("=", startselect);
endvalue = result.IndexOf(";", startvalue);
selectvalue = result.Substring(startvalue, endvalue - startvalue + 1);
newvalue = "= " + Settings.AppSettings.previewheight + " / c.h;";
result = result.Replace(selectvalue, newvalue);
//Configure Explicit Resizing
//配置显式调整大小
startselect = result.IndexOf("trueSize:");
startvalue = result.IndexOf("[", startselect);
endvalue = result.IndexOf("]", startvalue);
selectvalue = result.Substring(startvalue, endvalue - startvalue + 1);
newvalue = "[" + neww.ToString() + ", " + newh.ToString() + "]";
result = result.Replace(selectvalue, newvalue);
return result;
}
}
}
PassPhoto视图文件夹
Index.cshtml
@{
//从设置中读取已裁剪图像的默认值
// 缩小到一半
var prevw = Settings.AppSettings.previewwidth + "px";
var prevh = Settings.AppSettings.previewheight + "px";
//处理从控制器发回的值,以刷新UI
ViewBag.Title = "照片上传 - MVC";
var Image1W = prevw;
var Image1H = prevh;
var Image1src = "uploaded_images/raw/invalid.jpg";
var Imagename = "invalid.jpg";
var croppedpreviewLiteral = "";
var previewsrc = "uploaded_images/raw/invalid.jpg";
var displaypreview = "none";
var message = "";
var defaultfolder = "raw";
var uploadaction = "PassPhoto/UploadImage";
var cropaction = "PassPhoto/CropImage";
//消息确定清除,否则在UI中显示错误消息
if (ViewBag.Message != null)
{
message = ViewBag.Message;
if (message.StartsWith("OK"))
{
message = "";
}
}
if (ViewBag.Height != null)
{
Image1H = ViewBag.Height;
}
if (ViewBag.Width != null)
{
Image1W = ViewBag.Width;
}
if (ViewBag.ImageUrl != null)
{
Image1src = ViewBag.ImageUrl;
previewsrc = ViewBag.ImageUrl;
}
if (ViewBag.ImageName != null)
{
Imagename = ViewBag.ImageName;
}
if (ViewBag.PreviewDisplay != null)
{
displaypreview = ViewBag.PreviewDisplay;
if (displaypreview == "normal")
{
croppedpreviewLiteral = "预览图片:";
}
}
if (Image1src.ToLower().Contains("cropped"))
{
defaultfolder = "cropped";
}
if (ViewBag.rootPath != null)
{
Image1src = ViewBag.RootPath + Image1src;
previewsrc = ViewBag.RootPath + previewsrc;
uploadaction = ViewBag.RootPath + uploadaction;
cropaction = ViewBag.RootPath + cropaction;
}
}
<div>
<h3>概念证明 - 在线图像裁剪</h3>
使用下面的"浏览..."按钮选择图像,并在准备好后单击"上传图像"。
<br />
<span id="errorLiteral" style="font-size:14px; color:Red; font-weight:bold">
@message
</span>
<br />
<form action="@uploadaction" method="post" enctype="multipart/form-data">
<input id="fileupload" name="mypostedfile" type="file" />
<input type="submit" id="uploadfileButton" value="上传图片" />
</form>
<br />
<br />
<table>
<tr>
<td style="width:400px;">
<div id="imagenameLiteral">@Imagename</div>
<br /><br />
<div>
<img id="Image1" width="@Image1W" height="@Image1H" src="@Image1src" />
<br />
</div>
</td>
<td style="width:400px;">
<div id="croppedpreviewLiteral">@croppedpreviewLiteral</div>
<div style="width:@prevw;height:@prevh;overflow:hidden;background-color:#ffffff;display:@displaypreview">
<img id="preview" width="@prevw" height="@prevh" src="@previewsrc" />
</div>
</td>
</tr>
</table>
<form action="@cropaction" method="post" enctype="multipart/form-data">
<input id="X1value" name="X" type="hidden" />
<input id="Y1value" name="Y" type="hidden" />
<input id="X2value" name="X2" type="hidden" />
<input id="Y2value" name="Y2" type="hidden" />
<input id="Wvalue" name="W" type="hidden" />
<input id="Hvalue" name="H" type="hidden" />
<input id="filename" name="filename" value="@Imagename" type="hidden" />
<input id="folder" name="folder" value="@defaultfolder" type="hidden" />
<div id="cropinstructions" style="width:400px">
移动或重新排列原始图像内的选择工具,直到预览选项显示了你想保留的图像。准备好后,点击裁剪按钮,您的选择将会永久保存
<br /><br />
<input type="submit" id="cropimageButton" value="裁剪图像" />
</div>
</form>
</div>
@section scripts{
<script lang="Javascript">
@Html.Raw(ViewBag.PreviewJSMarkup)
</script>
}
_Layout.cshtml
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@ViewBag.Title - 我的ASP.NET应用程序</title>
@Styles.Render("~/bundles/css")
</head>
<body>
<div class="container body-content">
@RenderBody()
<hr />
<footer>
<p>© @DateTime.Now.Year - Wu</p>
</footer>
</div>
@Scripts.Render("~/bundles/jcrop")
@RenderSection("scripts", required: false)
</body>
</html>