1. 程式人生 > >MVC4實現圖片上傳,裁剪和儲存到伺服器

MVC4實現圖片上傳,裁剪和儲存到伺服器

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>&copy; @DateTime.Now.Year - Wu</p>
        </footer>
    </div>

    @Scripts.Render("~/bundles/jcrop")    
    @RenderSection("scripts", required: false)
</body>
</html>

執行結果如圖:

這裡寫圖片描述

這裡寫圖片描述

這裡寫圖片描述

這裡寫圖片描述