Mở đầu
Trong các bài viết trước đây các bạn đã biết ASP.NET Web API là gì và cách sử dụng nó trên ứng dụng như Windows Phone. Nếu bạn chưa biết có thể tham khảo lại các bài viết sau:
Trong bài viết này chúng ta sẽ cùng tìm hiểu về cách thức xử lý download và upload ảnh sử dụng ASP.NET Web API service trên ứng dụng ASP.NET MVC. Ứng dụng demo trong bài viết này là ứng dụng đổi avatar của người dùng, một phần không thể thiếu trong các ứng dụng social hiện nay.
Tạo ASP.NET Web API Service download upload ảnh dùng Base64 string
- Bước 1: Tạo project web WebAPIImageService và chọn template Web API như hình dưới
- Bước 2: Tạo mới một API Service đặt tên là AvatarController như bên dưới. Chúng ta chọn scaffold là WebAPI 2 Controller – Empty để viết code từ đầu không cần Visual Studio sinh sẵn code sample.
- Bước 3: Bổ sung 2 operation Get và Post theo đúng convention đặt tên của Web API vào AvatarController và sử dụng các hàm helper chuyển đổi giữa Image và string Base64 như sau:123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104namespace WebAPIImageService.Controllers{/// <summary>/// Service demo handling image download and upload by Web API service/// @Created by tungnt.net - 6/2015/// </summary>public class AvatarController : ApiController{/// <summary>/// Get Avatar image by userId/// </summary>/// <param name="userId"></param>/// <returns>@Created by tungnt.net - 6/2015</returns>public UserProfile Get(string userId){string imagePath;UserProfile userProfile = new UserProfile() { UserId = userId };try{imagePath = HttpContext.Current.Server.MapPath("~/Avatar/") + userId + ".jpg";if (File.Exists(imagePath)){using (Image img = Image.FromFile(imagePath)){if (img != null){userProfile.UserAvatarBase64String = ImageToBase64(img, ImageFormat.Jpeg);}}}}catch (Exception){userProfile.UserAvatarBase64String = null;}return userProfile;}private string ImageToBase64(Image image, ImageFormat format){string base64String;using (MemoryStream ms = new MemoryStream()){// Convert Image to byte[]image.Save(ms, format);ms.Position = 0;byte[] imageBytes = ms.ToArray();// Convert byte[] to Base64 Stringbase64String = Convert.ToBase64String(imageBytes);}return base64String;}/// <summary>/// Save image to Folder's Avatar/// </summary>/// <param name="userProfile"></param>/// <returns>@Created by tungnt.net - 6/2015</returns>public bool Post([FromBody]UserProfile userProfile){bool result = false;try{//For demo purpose I only use jpg file and save file name by userIdif (!string.IsNullOrEmpty(userProfile.UserAvatarBase64String)){using (Image image = Base64ToImage(userProfile.UserAvatarBase64String)){string strFileName = "~/Avatar/" + userProfile.UserId + ".jpg";image.Save(HttpContext.Current.Server.MapPath(strFileName), ImageFormat.Jpeg);result = true;}}}catch (Exception){result = false;}return result;}private Image Base64ToImage(string base64String){// Convert Base64 String to byte[]byte[] imageBytes = Convert.FromBase64String(base64String);Bitmap tempBmp;using (MemoryStream ms = new MemoryStream(imageBytes, 0, imageBytes.Length)){// Convert byte[] to Imagems.Write(imageBytes, 0, imageBytes.Length);using (Image image = Image.FromStream(ms, true)){//Create another object image for dispose old image handlertempBmp = new Bitmap(image.Width, image.Height);Graphics g = Graphics.FromImage(tempBmp);g.DrawImage(image, 0, 0, image.Width, image.Height);}}return tempBmp;}}}
Ở đây chúng ta sử dụng class UserProfile để lưu thông tin người dùng truyền qua lại giữa client và server và với mục đích demo nên chúng ta sẽ lưu ảnh vào một thư mục tên là Avatar trên server nên nếu chưa có bạn vui lòng tạo ra thư mục này trước.123456789namespace WebAPIImageService.Controllers{public class UserProfile{public string UserId { get; set; }public string UserAvatarBase64String { get; set; }}}
Bên cạnh đó để cho đơn giản chúng ta sẽ lưu tên file ảnh avatar theo userid (số hoặc email chẳng hạn) và fix định dạng ảnh hỗ trợ là jpg. Các bạn có thể mở rộng thêm các tính năng như lưu avatar vào database thay vì file và hỗ trợ thêm các định dạng ảnh khác. - Bước 4: Kiểm tra Web API đã chạy tốt hay chưa bằng cách rebuild lại ứng dụng và Ctrl + F5 để chạy thử và kiểm tra phần help API.
Đến đây các bước chuyển bị service đã hoàn tất, nhìn chúng các bước xây dựng Web API service khá đơn giản và không mất nhiều công sức cấu hình so với WCF REST service, các bạn có thể so sánh bài tương tự sử dụng WCF REST Service trong link bên dưới để thấy rõ hơn về điều này:
Bây giờ chúng ta sẽ cùng sang phần tiếp theo trên phía client
Kết nối Web API Service từ ứng dụng ASP.NET MVC sử dụng jQuery AJAX
Ở phần trên ta đã xây dựng xong Web API service phía server trong phần này chúng ta sẽ cùng xây dựng một ứng dụng phía client sử dụng các web service này. Ứng dụng demo cho phép lấy về hiển thị avatar và đổi avatar của người dùng theo userid truyền vào. Công nghệ chúng ta sẽ sử dụng ở đây là ASP.NET MVC và jQuery AJAX. Chúng ta sẽ cùng thực hiện qua ba bước như sau:
- Bước 1: Thiết kế giao diện của trang đổi Avatar người dùng, chúng ta sẽ sử dụng luôn file Index.cshtml đã được sinh ra khi tạo project Web API ở phần trên. Giao diện của ứng dụng sẽ gồm:
- 1 div để hiển thị thông tin về ứng dụng
- 1 form download/upload avatar chứa: 1 image, 1 input text để nhập userId của người dùng, 2 input button để download và upload ảnh.
123456789101112131415<div class="jumbotron"><p class="lead">Using Web API Service in ASP.NET MVC: User Profile Avatar Change Demo</p></div><form role="form"><div class="form-group"><img alt="Avatar" src="~/Avatar/anonymous.gif" class="img-rounded" width="128" height="128" id="imgAvatar" /></div><div class="form-group form-inline"><input id="txtUserId" type="text" class="form-control" placeholder="Enter UserId to get or set avatar" /></div><div class="form-group form-inline"><input id="btnGetUserAvatar" type="button" width="128" class="btn btn-primary" value="Get Avatar" /><input id="btnSetUserAvatar" type="button" width="128" class="btn btn-primary" value="Set Avatar" /></div></form>Ở đây chúng ta sử dụng các class của Bootstrap để thiết kế giao diện cho đẹp và chuyên nghiệp. Nếu bạn chưa biết về Bootstrap thì có thể hiểu đơn giản Bootstrap là một CSS framework giúp chúng ta xây dựng các giao diện web rất nhanh chóng dễ dàng, có khả năng responsive và cross-browser. Các bạn có thể tìm hiểu thêm về Bootstrap tại đây: http://getbootstrap.com/
- Bước 2: Sau khi đã có giao diện chúng ta sẽ bắt tay viết phần download avatar người dùng sử dụng jQuery trước. jQuery là một thư viện JavaScript tương đối mạnh mẽ và dễ sử dụng so với JavaScript thuần. Chi tiết về cách thức sử dụng jQuery chúng ta sẽ cùng nghiên cứu ở một bài viết khác, nếu muốn bạn có thể tham khảo thêm về jQuery ngay tại đây: http://jquery.com/Để download ảnh avatar trước tiên chúng ta cần bổ sung vào trong phần @section scripts hàm AjaxRequest helper với các tham số (tên phương thức web api service, phương thức Http: GET/POST/PUT/DELETE, dữ liệu truyền qua body request, hàm callback được gọi khi thực hiện xong request)1234567891011121314151617//AJAX Request helper method//Created by tungnt.net - 6/2015function AjaxRequest(methodName, methodType, data, callback) {$.ajax({type: methodType,url: "api/" + methodName,data: JSON.stringify(data),contentType: "application/json; charset=utf-8",dataType: "json",success: function (result, status, xhr) {//When success the callback function is calledif (callback) {callback(result);}}});}
Tiếp theo chúng ta viết hàm getUserAvatar gọi đến AjaxRequest và gán cho sự kiện onlick của input button btnGetUserAvatar như sau:1<input id="btnGetUserAvatar" type="button" width="128" class="btn btn-primary" value="Get Avatar" onclick="getUserAvatar();" />12345//Download User Avatar//Created by tungnt.net - 6/2015function getUserAvatar() {AjaxRequest("Avatar?userId=" + $("#txtUserId").val(), "GET", {}, getCompletedUserAvatar);}
Cuối cùng ta viết hàm callback getCompletedUserAvatar để hiển thị ảnh lên trên image hoặc hiển thị cảnh báo nếu không có ảnh avatar của người dùng với userId được nhập vào. Ở đây để hiển thị ảnh trên phía server đã trả về dữ liệu dạng Base64 string nên ở client đơn giản ta chỉ cần sử dụng định dạng inline Image data:image/jpg;base64 và gán cho image src là sẽ hiển thị được ảnh ngay.123456789//Callback when complete download avatar//Created by tungnt.net - 6/2015function getCompletedUserAvatar(data) {if (data.UserAvatarBase64String !== null) {$("#imgAvatar").attr("src", "data:image/jpg;base64," + data.UserAvatarBase64String);} else {showAlertInfo("alert-danger", "No user have this UserId. Please check again!");}}
Với hàm cảnh báo khi không tìm được ảnh theo userid ta sử dụng animation fadeTo và slideUp của jQuery cho thêm phần sinh động. Ngoài ra ta cần thiết kế thêm 1 div ở trên đầu form để hiển thị các cảnh báo này.123@*Notification Alert Container*@<div id="notification"></div>12345678//Show alert info helper method//Created by tungnt.net - 6/2015function showAlertInfo(alertClass, alertMessage) {$('#notification').html("<div class='alert-info'><div class='alert " + alertClass + " alert-dismissible fade in'><button type='button' class='close' data-dismiss='alert' aria-label='Close'><span aria-hidden='true'>×</span></button><strong>" + alertMessage + "</strong></div></div>");$("#notification").fadeTo(2000, 500).slideUp(500, function () {$('#alert-info').alert('close');});}
Để test thử ta chỉ cần để một ảnh jpg bất kì trong thư mục Avatar trên phía server và nhấn button Get Avatar để hiển thị lên image.
- Bước 3: Phần download ảnh đã hoàn tất ta thực hiện phần cuối cùng là Upload ảnh lên web api serviceĐể có thể upload image chúng ta cần bổ sung thêm một thẻ input dạng file, để cho giao diện đẹp ta không trực tiếp sử dụng thẻ này mà sẽ sử dụng nó qua một thẻ input button khác. Chúng ta thiết kế lại giao diện phần này bổ sung thêm thẻ input dạng file và một thẻ input button, đồng thời thêm các style CSS và viết hàm javascript để khi di chuyển chuột vào và ra (hover) image sẽ hiển thị button Chọn ảnh lên cho chuyên nghiệp.12345678910@*Avatar Container*@<div class="avatarDiv"><div class="caption"><p><input type="button" class="btn btn-warning text-center" onclick="showImagePreview();" value="Choose Avatar"/></p></div><img alt="Avatar" src="~/Avatar/anonymous.gif" class="img-rounded" width="128" height="128" id="imgAvatar" /><input id="iUserAvatar" type="file" style="height:0; width:0; visibility:hidden" /></div>1234567891011121314151617181920<style>.avatarDiv {position: relative;width: 128px;height: 128px;}.caption {display: none;position: absolute;background: rgba(0, 0, 0, 0.4);width: 128px;height: 128px;line-height: 128px;}.caption p {padding: 4px;}</style>1234567891011$(document).ready(function () {//Show-hide button choose avatar when hover image.$(function () {$('.avatarDiv').hover(function () {$(this).find('.caption').slideDown(250);},function () {$(this).find('.caption').slideUp(250);});});});
Tiếp theo ta viết hàm showPreviewImage cho sự kiện onlick của button Chọn ảnh Choose Avatar. Trong hàm này ta sẽ kích hoạt dialog chọn ảnh của input file, khi chọn ảnh xong ta sẽ sử dụng FileReader để chuyển ảnh thành dạng Base64 string qua phương thức readAsDataURL và gán vào source của image để preview được ảnh ngay.
12345678910111213141516171819//Choose image from disk and preview before upload//Created by tungnt.net - 6/2015function showImagePreview() {var fileInput = $("#iUserAvatar");if (fileInput !== null) {//Open file dialog to choose imagefileInput.trigger("click");fileInput.change(function () {if (this.files && this.files[0]) {//When choose image complete using FileReader to convert image to Base64 stringvar reader = new FileReader();reader.onload = function (e) {$('#imgAvatar').attr('src', e.target.result);}reader.readAsDataURL(this.files[0])}});}}Sau khi chọn ảnh xong người dùng cần nhập UserId cho input text và nhấn button Set Avatar để upload ảnh lên server. Ta cần viết hàm setUserAvatar này và gán vào sự kiện onclick của button btnSetUserAvatar. Ở hàm này đơn giản chúng ta chỉ cần tạo ra đối tượng userProfile với hai tham số là userId người dùng đã nhập và ảnh dạng Base64 được trích ra từ source của ảnh preview ở phần trên. Sau đó chúng ta sử dụng AjaxRequest để upload ảnh lên server và gọi hàm callback hiển thị thông báo thành công khi upload xong.
12345678910111213141516//Upload avatar to server//Created by tungnt.net - 6/2015function setUserAvatar() {var userObject = { };userObject.UserId = $("#txtUserId").val();userObject.UserAvatarBase64String = $("#imgAvatar").attr("src").split(",")[1];AjaxRequest("Avatar", "POST", userObject, setAvatarCompleted);}//Callback when complete upload avatar//Created by tungnt.net - 6/2015function setAvatarCompleted(data) {if (data === true) {showAlertInfo("alert-success", "User profile avatar is saved successful!");}}Đến đây mọi thứ đã hoàn tất các bạn có thể chạy thử ứng dụng bằng Ctrl+F5 để hưởng thụ thành quả vừa xây dựng xong.
Kết luận
Trong bài viết này tôi đã hướng dẫn các bạn cách xây dựng web service xử lý download và upload ảnh sử dụng ASP.NET Web API Service dùng Base64 String từng bước hết sức chi tiết. Ứng dụng phía client sử dụng công nghệ ASP.NET MVC được thiết kế bằng css framework Bootstrap trông khá chuyên nghiệp cùng với jQuery để thực hiện các lời gọi AJAX tới Web API.
Như các bạn đã thấy Web API đơn giản hóa rất nhiều thao tác so với WCF REST Service vì không còn phải cấu hình loằng ngoằng và phức tạp như WCF nữa. Tất nhiên tùy từng trường hợp cụ thể mà bạn có thể sử dụng WCF hoặc Web API vì chúng đều đáp ứng được, tuy nhiên nếu bạn xây dựng các web service cho nhiều nền tảng khác nhau nhất là các nền tảng cho ứng dụng mobile thì lời khuyên của tôi đó là nên chọn Web API vì sự đơn giản hiệu quả của nó cộng với sự hỗ trợ mạnh mẽ từ Microsoft ở thời điểm hiện nay. Trong các bài viết kế tiếp chúng ta sẽ cùng tìm hiểu cách thức xây dựng và sử dụng các service bằng các công nghệ khác như ServiceStack…
Hy vọng bài viết này sẽ giúp ích cho các bạn trong quá trình xây dựng các ứng dụng của riêng mình. Nếu bạn có bất kì câu hỏi hay kinh nghiệm nào hãy để lại comment bên dưới bài viết và đừng quên chia sẻ cho bạn bè bài viết này nếu thấy hữu ích nhé.
Happy coding. Stay tuned.
P/s: Source code example trong bài các bạn có thể download tại đây: WebAPIImageDemo
Gửi anh Tùng
Ngoài cách sử dụng kiểu application/json để post lên web api thì còn có kiểu multipart/form-data nữa, không biết là kiểu nào có performance cao hơn và được dùng nhiều hơn anh nhỉ??? em có đọc 1 vài diễn đàn thì họ nói là sài Base64 thì chất lượng hình ảnh sẽ giảm xuống chỉ còn 30% (hay 70% không nhớ lắm)so với ảnh gốc, còn kiểu kia thì không thay đổi
Chào Trường,
Dùng Base64String thì đơn giản hơn nhưng phải trả giá là độ lớn của string bị tăng thêm 33% so với ảnh gốc chứ không phải giảm dung lượng ảnh. Em dùng multi-part thì dung lượng không đổi nhưng sẽ phải xử lý khó hơn và nhiều hơn so với Base64String. Cái này tùy thuộc bài toán mà quyết định thôi. Khi nào cần upload nhiều file cần performance cao thì dùng multi-part khi nào cần upload chỉ 1 file thì có thể dùng base64.
Regards.
Cảm ơn anh đã phản hồi ^^
Hi Anh Tùng .. em đang làm 1 trang web. Vấn đề đặt ra là em đã bóc tách được 1 loạt các link các bức ảnh từ các trang khác. Em đã bin dữ liệu được rồi nhưng khi chạy lên em không thể nào hiển thị được ảnh trên trang web của em.. Em đã thử với cả Web Form và Web MVC đều ko thể hiển thị được ảnh.. Khi em ấn thử F12 trên trình duyệt Web xem các thẻ bin ảnh của em… giá trị của thuộc tính src là 1 đường link chuẩn xác hiển thị được.. ko hiểu sao thẻ Img với thuộc tính src của em lại ko thể nào hiển thị được .. Anh giúp em với ạ 😀 . Thank anh nhiều ạ
Hi Mạnh, nói thế này anh rất khó hình dung em gặp phải lỗi gì. Vui lòng post code hoặc gửi code sample cho anh để anh review giúp.
Chào anh Tùng !
Anh cho em hỏi là nếu gửi Image mà không đưa về base64string mà gửi theo file thì bên server mình làm sao để nhận được ạ ? Sử dụng method POST và kiểu gửi lên là JSON. Em từng làm qua với form-data nhưng nếu dùng raw gửi lên thì vẫn chưa nhận được
Mong anh giúp đỡ.
Trân trọng
Bài viết rất hay. Thankyou
Cảm ơn bạn,
Ủng hộ blog của mình bằng cách subscribe bài viết mới nhé.