MVC4實現圖片上傳,裁剪和儲存到伺服器
阿新 • • 發佈:2019-01-27
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>