前言
附件使用的Dynamics CRM平臺本身的註釋表annotation儲存,將附件轉換成二進位制位元組流儲存到資料庫中,因自帶的註釋在頁面中顯示附件不夠直觀,特做了一個單獨的附件管理自定義頁面,透過CRM自定義按鈕開啟對話方塊的方式展示附件列表頁面。同時支援下載附件模板,頁面為簡單的H5+Bootstrap+CSS佈局設計,透過ajax呼叫webAPI介面實現上傳、下載、刪除等操作,本文一併附上後臺介面程式碼。
注意:本文中實現上傳不支援多檔案同時上傳,需要多檔案同時上傳,可找現成的前端檔案上傳元件,本文中透過input型別'file'傳遞給後臺介面的檔案只支援接收一個檔案,多檔案上傳需要修改下對應的後臺上傳介面,增加引數HttpPostedFileBase[] files,並在處理檔案的邏輯改成遍歷迴圈處理即可,其他內容一致。
檔案上傳方式
html頁面中一個選擇檔案的input,type型別為'file',再一個上傳檔案的button按鈕,然後做一個表格用來展示已上傳的附件列表。選擇完檔案後,點選上傳按鈕,獲取input中選擇的file,js組裝new FormData()物件,賦值後臺介面所需要的引數,然後ajax呼叫webapi介面,注意一定要設定processData: false和contentType: false,不然後臺介面接收不到檔案。後臺介面將接受的檔案轉成流儲存到資料庫中。
其他的檔案下載和刪除就不介紹了,比較簡單。
效果圖
程式碼
自定義按鈕開啟對話方塊頁面
1 /** 2 * 附件管理操作方法 3 */ 4 function attachment() { 5 console.log("附件") 6 if (Xrm.Page.data.entity.getIsDirty()) { 7 Xrm.Utility.alertDialog("請先儲存後再上傳附件!") 8 return 9 } 10 let EntityId = commonUtil.delBrackets(Xrm.Page.data.entity.getId()) 11 let EntityName = Xrm.Page.data.entity.getEntityName() 12 13 let params = { 'id': EntityId, 'type': EntityName} 14 15 var DialogOption = new Xrm.DialogOptions 16 DialogOption.width = 900; 17 DialogOption.height = 650; 18 // 引數一:URL,引數二:窗體配置,引數三:Json引數,引數四:--,引數五:-- 19 Xrm.Internal.openDialog("/WebResources/foton_accountchannelaccess_attachment", DialogOption, params, null, function (returnValue) { 20 console.log('呼叫成功 返回值:' + returnValue); //這裡就可以接收到彈窗上面傳過來的陣列 21 }); 22 }
附件管理HTML
1 <!DOCTYPE html> 2 3 <html lang="en" xmlns="http://www.w3.org/1999/xhtml"> 4 <head> 5 <meta charset="utf-8" /> 6 <title>附件管理</title> 7 <script src="foton_Jquery.min.js"></script> 8 <script src="ClientGlobalContext.js.aspx"></script> 9 <script src="foton_kd_base_js"></script> 10 <link href="foton_Newbootstrap.min.css" rel="stylesheet"> 11 <link href="foton_componentsrounded.min.css" rel="stylesheet"> 12 <link href="foton_components.min.css" rel="stylesheet"> 13 <style type="text/css"> 14 .fileinput-button input { 15 position: static; 16 opacity: 1; 17 filter: none; 18 font-size: inherit; 19 direction: inherit; 20 } 21 22 .fileinput-button span { 23 display: none; 24 } 25 </style> 26 </head> 27 <body style="overflow-wrap: break-word;"> 28 <div class="col-md-12" style="width: 100%; height: 100%;"> 29 <div class="portlet box portlet light portlet-fit bordered" style="height: 89%; margin-top: 40px;"> 30 <div class="portlet-body" style="height: 15%;"> 31 <div class="row"> 32 <div class="col-md-8">商業計劃書新籤</div> 33 <div class="col-md-4" style=" float: right"><label style="color: red;" for="lb_cl1">點選下載模板</label></div> 34 </div> 35 <div class="row"> 36 <div class="col-md-8">商業計劃書續簽</div> 37 <div class="col-md-4" style=" float: right"><label style="color: red;" for="lb_cl2">點選下載模板</label></div> 38 </div> 39 <div class="row"> 40 <div class="col-md-8">渠道基本資訊評價表</div> 41 <div class="col-md-4" style=" float: right"><label style="color: red;" for="lb_cl3">點選下載模板</label></div> 42 </div> 43 <div class="row"> 44 <div class="col-md-8">海外網路退出要素確認表</div> 45 <div class="col-md-4" style=" float: right"><label style="color: red;" for="lb_cl4">點選下載模板</label></div> 46 </div> 47 <div class="row"> 48 <div class="col-md-8">公司簡介</div> 49 <div class="col-md-4"><label style="color: red;"></label></div> 50 </div> 51 </div> 52 <div class="portlet-title" style="height: 5%;"> 53 <span> 54 <label style="color: red;">請先下載模板,根據模板維護資料後再上傳附件!</label> 55 </span> 56 <!--<form id='fileupload' action='/Mpa/Annotation/UpLoadAttmention' method='POST' enctype='multipart/form-data'> 57 </form>--> 58 <div class='row fileupload-buttonbar'> 59 <input type='hidden' id='FileName' name='FileName' value='' /> 60 <input type='hidden' id='UploadType' name='UploadType' value='' /> 61 <div class='col-lg-7 btnDelete' id='btnFileUploadId' style='margin-left: 30px; float: right'> 62 <span class='btn green fileinput-button'> 63 <i class='fa fa-plus'></i> 64 <span>選擇檔案</span> 65 <input type='file' name='fileUpload' id='fileUpload' onchange='document.getElementById("FileName").value = this.value.substr(this.value.lastIndexOf("\\") + 1)'> 66 </span> 67 <button type='button' class='btn blue start' onclick='SaveFiles()'> 68 <i class='fa fa-upload'></i> 69 <span> 上傳檔案 </span> 70 </button> 71 </div> 72 </div> 73 </div> 74 75 <div class="portlet-body table-scrollable" style="height: 80%; overflow: auto; max-height: 800px; padding: 0 10px"> 76 <table class="table table-bordered table-hover"> 77 <thead> 78 <tr id="opp"> 79 <th width="150" style='vertical-align: middle;text-align: center;'> 檔名稱 </th> 80 <th width="150" style='vertical-align: middle;text-align: center;'> 檔案型別 </th> 81 <th width="150" style='vertical-align: middle;text-align: center;'> 建立時間 </th> 82 <th width="200" style='vertical-align: middle;text-align: center;'> 操作 </th> 83 </tr> 84 </thead> 85 <tbody id="Dataid"></tbody> 86 </table> 87 </div> 88 </div> 89 </div> 90 91 92 <script type="text/javascript"> 93 let entityId = window.getDialogArguments().id 94 let entityName = window.getDialogArguments().type 95 let queryStr = "" 96 var apiUrl = "" 97 var fileObj = document.getElementById("fileUpload") 98 var outerHTML = fileObj.outerHTML; 99 //頁面載入 100 $(function () { 101 setWebAPIURL() 102 initOnload() 103 var label1 = $("label[for='lb_cl1']"); 104 label1.on("click", function () { 105 console.log("你點選了label1標籤"); 106 downloadTemplate(1) 107 }); 108 var label2 = $("label[for='lb_cl2']"); 109 label2.on("click", function () { 110 console.log("你點選了label2標籤"); 111 downloadTemplate(2) 112 }); 113 var label3 = $("label[for='lb_cl3']"); 114 label3.on("click", function () { 115 console.log("你點選了label3標籤"); 116 downloadTemplate(3) 117 }); 118 var label4 = $("label[for='lb_cl4']"); 119 label4.on("click", function () { 120 console.log("你點選了label4標籤"); 121 downloadTemplate(4) 122 }); 123 }) 124 //設定webapi請求地址 125 function setWebAPIURL() { 126 if (Xrm.Page.context.getClientUrl().indexOf("Testf") >= 0)//測試環境 127 apiUrl = "http://localhost:50887/"; //測試:http://10.100.56.13:8001/ ,本地:http://localhost:50887/ 128 else if (Xrm.Page.context.getClientUrl().indexOf("hwdms") >= 0)//正式環境 129 apiUrl = "https://hwapi.foton.com.cn/"; 130 } 131 /** 132 * 頁面初始化載入 133 */ 134 function initOnload() { 135 //獲取客戶經銷商窗體下的所有附件 136 queryStr = `/annotations?$select=annotationid,createdon,documentbody,filename,filesize,mimetype,_objectid_value,objecttypecode,subject&$filter=_objectid_value eq ${entityId}&$orderby=createdon desc` 137 commonUtil.queryWithUrl(queryStr, result => { 138 $("#Dataid").empty(); 139 if (!result.success || !result.data || result.data.length < 1) { 140 console.log("當前客戶尚未上傳附件!") 141 return 142 } 143 console.log(result.data) 144 for (var i = 0; i < result.data.length; i++) { 145 var html = "<tr class='success'>" 146 + "<td style='width: 5px;display:none;'> <input type='hidden' name='annotation_id"+ i+"' value='" + result.data[i]["annotationid"] + "'/> </td>"; 147 //檔名稱 148 if (!!result.data[i]["filename"]) { 149 html += "<td width='150px;' align='center' valign='middle' title='" + result.data[i]["filename"] + "'> " + result.data[i]["filename"] + " </td>"; 150 } else { 151 html += "<td width='150px;'></td>"; 152 } 153 //檔案型別 154 if (!!result.data[i]["mimetype"]) { 155 html += "<td width='150px;' align='center' valign='middle' title='" + result.data[i]["mimetype"] + "'> " + result.data[i].filename.substr(result.data[i].filename.lastIndexOf(".") + 1) + " </td>"; 156 } else { 157 html += "<td width='150px;'></td>"; 158 } 159 //建立時間 160 if (!!result.data[i]["createdon"]) { 161 html += "<td width='150px;' align='center' valign='middle' title='" + result.data[i]["createdon"] + "'> " + result.data[i]["createdon@OData.Community.Display.V1.FormattedValue"] + " </td>"; 162 } else { 163 html += "<td width='150px;'></td>"; 164 } 165 //操作 166 html += "<td width='200px;' align='center' valign='middle'>" 167 + "<button class='btn blue start' type='button' onclick=DownloadFile('" + result.data[i].annotationid + "');>" 168 + "<i class='fa fa-download'></i><span>下載</span>" 169 + "</button> " 170 + "<button class='btn red cancel obsbtnDelete' onclick= \"DeleteFile('" + result.data[i].annotationid + "')\"><i class='fa fa-trash'></i><span>刪除</span></button>" 171 + "</td> "; 172 173 html += "</tr>"; 174 175 $("#Dataid").append(html + "<br/>"); 176 } 177 178 }, false) 179 180 } 181 /* 182 * 呼叫介面上傳檔案 183 */ 184 function SaveFiles(callback) { 185 var files = document.getElementById("fileUpload").files[0] 186 if (files.length < 1) { 187 Xrm.Utility.alertDialog("請先選擇檔案!") 188 return 189 } 190 if (!callback) { 191 overflowLayer.open("上傳附件中....", SaveFiles) 192 return 193 } 194 var formFile = new FormData(); 195 formFile.append("entityId", entityId);//實體資料id 196 formFile.append("entityName", entityName);//實體名 197 formFile.append("files", files); //加入檔案物件 198 $.ajax({ 199 url: apiUrl + "AccountChannelAccess/SaveAnnotation", 200 type: "post", 201 data: formFile, 202 dataType: 'json', 203 //mimeType: "multipart/form-data", 204 async: true, //使用同步的方式,true為非同步方式 205 processData: false, 206 contentType: false, 207 success: function (data, textStatus, xhr) { 208 overflowLayer.close(); 209 if (data.code == 0) { 210 Xrm.Utility.alertDialog(data.msg); 211 } else { 212 Xrm.Utility.alertDialog("上傳成功"); 213 //重新整理頁面 214 initOnload() 215 //清除選擇input裡已上傳的檔案 216 fileObj.outerHTML = outerHTML 217 } 218 }, 219 error: function (xhr, textStatus, errorThrown) { 220 Xrm.Utility.alertDialog("請求介面【" + apiUrl + "】失敗!請聯絡管理員!"); 221 overflowLayer.close(); 222 } 223 }); 224 } 225 /** 226 * 刪除已上傳的檔案 227 * @param {any} annotationId 附件id 228 */ 229 function DeleteFile(annotationId) { 230 Xrm.Utility.confirmDialog("您確認要刪除此附件嗎?", function () { 231 $.ajax({ 232 url: apiUrl + "AccountChannelAccess/DelAnnotation", 233 type: "post", 234 data: { "": annotationId }, 235 dataType: 'json', 236 async: false, //使用同步的方式,true為非同步方式 237 success: function (data, textStatus, xhr) { 238 if (data.code == 0) { 239 Xrm.Utility.alertDialog("刪除失敗!" + data.msg); 240 } 241 Xrm.Utility.alertDialog("刪除成功!"); 242 initOnload() 243 }, 244 error: function (xhr, textStatus, errorThrown) { 245 Xrm.Utility.alertDialog("請求介面【" + apiUrl + "】失敗!請聯絡管理員!") 246 } 247 }); 248 }); 249 } 250 /* 251 * 下載檔案 252 * @param {any} annotationId 附件id 253 */ 254 function DownloadFile(annotationId) { 255 console.log(annotationId); 256 location.href = apiUrl + 'AccountChannelAccess/DownloadAnnotation?id=' + annotationId; 257 } 258 /** 259 * 下載附件模板 260 * @param {any} type 1商業計劃書新籤,2商業計劃書續簽,3渠道基本資訊評價表,4海外網路退出要素確認表 261 */ 262 function downloadTemplate(type) { 263 //請求webAPI介面下載模板檔案 264 location.href = apiUrl + 'AccountChannelAccess/DownloadFileTemplate?type=' + type; 265 } 266 </script> 267 </body> 268 </html>
WebAPI介面程式碼
1 using Microsoft.Xrm.Sdk; 2 using Microsoft.Xrm.Sdk.Query; 3 using Newtonsoft.Json; 4 using NPOI.OpenXml4Net.OPC.Internal; 5 using System; 6 using System.Collections.Generic; 7 using System.Data.Entity.Core.Objects.DataClasses; 8 using System.IO; 9 using System.Linq; 10 using System.Net; 11 using System.Net.Http; 12 using System.Net.Http.Headers; 13 using System.Reflection; 14 using System.Text; 15 using System.Threading.Tasks; 16 using System.Web; 17 using System.Web.Http; 18 using ZZ_Common_API.Crm; 19 using ZZ_Common_API.Models.QDRWModels; 20 using EntityReference = Microsoft.Xrm.Sdk.EntityReference; 21 22 namespace ZZ_Common_API.Controllers.QDRWControllers 23 { 24 /// <summary> 25 /// 附件相關WebAPI介面 26 /// </summary> 27 [RoutePrefix("AccountChannelAccess")] 28 public class AccountChannelAccessController : ApiController 29 { 30 #region 下載模板檔案 strat 31 /// <summary> 32 /// 下載附件 33 /// </summary> 34 /// <param name="type">1商業計劃書新籤,2商業計劃書續簽,3渠道基本資訊評價表,4海外網路退出要素確認表</param> 35 /// <returns></returns> 36 [Route("DownloadFileTemplate")] 37 [HttpGet] 38 public async Task<dynamic> DownloadFileTemplate(int type) 39 { 40 HttpResponseMessage responseMessage = new HttpResponseMessage(HttpStatusCode.OK); 41 string fileName = string.Empty; 42 switch (type) 43 { 44 case 1: 45 fileName = "附件1:商業計劃書新籤(可下載模版).pptx"; 46 break; 47 case 2: 48 fileName = "附件2:商業計劃書續簽(可下載模版).pptx"; 49 break; 50 case 3: 51 fileName = "附件3:渠道基本資訊評價表(可下載模版).docx"; 52 break; 53 case 4: 54 fileName = "附件4:海外網路退出要素確認表(可下載模板).docx"; 55 break; 56 default: 57 fileName = "附件1:商業計劃書新籤(可下載模版).pptx"; 58 break; 59 } 60 61 var filePath = AppDomain.CurrentDomain.BaseDirectory + @"\Files\Template\" + fileName; 62 if (!File.Exists(filePath)) 63 { 64 return ResponseMessage(new HttpResponseMessage() 65 { 66 Content = 67 new StringContent(JsonConvert.SerializeObject(Json(new { code = 0, msg = $"下載失敗,未能找到檔案-{fileName}的路徑" })), 68 Encoding.GetEncoding("UTF-8"), "application/json"), 69 StatusCode = HttpStatusCode.NoContent 70 }); 71 } 72 var stream = new FileStream(filePath, FileMode.Open); 73 responseMessage.Content = new StreamContent(stream); 74 responseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); 75 HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment;filename=" + HttpUtility.UrlPathEncode(fileName)); 76 return ResponseMessage(responseMessage); 77 } 78 79 80 #endregion 下載模板檔案 end 81 82 #region 上傳附件 start 83 /// <summary> 84 /// 使用系統的註釋Annotation 新增附件 85 /// </summary> 86 /// <returns></returns> 87 [Route("SaveAnnotation")] 88 [HttpPost] 89 public object SaveAnnotation() 90 { 91 try 92 { 93 HttpFileCollection fileCollection = HttpContext.Current.Request.Files; 94 if (fileCollection.Count <= 0) 95 { 96 return Json(new { code = 0, msg = $"請先選擇檔案後點選上傳" }); 97 } 98 var formData = HttpContext.Current.Request.Form; 99 var entityId = formData["entityId"]; 100 var entityName = formData["entityName"]; 101 102 Guid objId = Guid.Empty; 103 if (!Guid.TryParse(entityId, out objId)) //檢驗實體id是否合法 104 { 105 return Json(new { code = 0, msg = $"上傳失敗,entityId是不合法的GUID:{entityId}" }); 106 } 107 HttpPostedFile formFile = fileCollection[0]; 108 if (formFile.ContentLength > 1048576000)//檢查檔案大小 109 { 110 return Json(new { code = 0, msg = $"{formFile.FileName}檔案大小不得超過{1048576000 / (1024f * 1024f)}M" });//請求體過大,檔案大小超標 111 } 112 var suffix = Path.GetExtension(formFile.FileName);//提取上傳的檔案檔案字尾 113 if (".js;.bat;.exe;.sh".IndexOf(suffix) > 0) //檢查檔案格式 114 { 115 return Json(new { code = 0, msg = $"不支援此檔案型別-{suffix}" });//型別不正確 116 } 117 118 AnnotationModel model = new AnnotationModel(); 119 model.Subject = Path.GetFileName(formFile.FileName).Substring(1); 120 model.NoteText = ""; 121 model.ObjectId = objId; 122 model.ObjectIdName = entityName; 123 model.MimeType = formFile.ContentType; 124 model.FileSize = formFile.ContentLength; 125 model.FileName = Path.GetFileName(formFile.FileName); 126 model.DocumentBody = Convert.ToBase64String(GetFileByte(formFile)); 127 //儲存到Annotation中 128 CreateAnnotation(model); 129 130 return Json(new { code = 1, msg = "上傳成功" }); 131 } 132 catch (Exception ex) 133 { 134 return Json(new { code = 0, msg = $"上傳失敗 + {ex.Message}" }); 135 } 136 } 137 #endregion 上傳附件 end 138 139 #region 下載附件 start 140 /// <summary> 141 /// 下載附件 142 /// </summary> 143 /// <param name="id">附件id</param> 144 /// <returns></returns> 145 [Route("DownloadAnnotation")] 146 [AcceptVerbs("GET")] 147 public IHttpActionResult DownloadAnnotation(string id) 148 { 149 //透過附件id 找到附件 然後轉化附件位元組流 填入到響應中 150 HttpResponseMessage responseMessage = new HttpResponseMessage(HttpStatusCode.OK); 151 try 152 { 153 if (string.IsNullOrEmpty(id)) 154 { 155 return ResponseMessage(new HttpResponseMessage(HttpStatusCode.Gone)); 156 } 157 Tuple<string, Stream> tuple = GetAnnotationStream(id); 158 if (tuple.Item1 == "未能獲取到位元組流") 159 { 160 return ResponseMessage(new HttpResponseMessage(HttpStatusCode.Gone)); 161 } 162 responseMessage.Content = new StreamContent(tuple.Item2); 163 responseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); 164 HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment;filename=" + HttpUtility.UrlPathEncode(tuple.Item1)); 165 } 166 catch (Exception ex) 167 { 168 return ResponseMessage(new HttpResponseMessage() 169 { 170 Content = 171 new StringContent(JsonConvert.SerializeObject(Json(new { code = 0, msg = $"下載失敗-{ex.Message}" })), 172 Encoding.GetEncoding("UTF-8"), "application/json"), 173 StatusCode = HttpStatusCode.NoContent 174 }); 175 } 176 return ResponseMessage(responseMessage); 177 } 178 #endregion 下載附件 end 179 180 #region 根據實體id獲取實體資料下相關所有附件,打包成zip檔案下載 start 181 /// <summary> 182 /// 下載實體資料下所有的附件zip 183 /// 根據實體id獲取實體資料下相關所有附件,打包成zip檔案下載 184 /// </summary> 185 /// <param name="id"></param> 186 /// <returns></returns> 187 [Route("DownloadAnnotationAll")] 188 [AcceptVerbs("GET")] 189 public IHttpActionResult DownloadAnnotationAll(string id) 190 { 191 HttpResponseMessage responseMessage = new HttpResponseMessage(HttpStatusCode.OK); 192 try 193 { 194 if (string.IsNullOrEmpty(id)) 195 { 196 return ResponseMessage(new HttpResponseMessage(HttpStatusCode.Gone)); 197 } 198 //透過模組id 找到相關的所有附件 然後將所有的附件打包成zip檔案 將zip檔案轉化位元組流 填入到響應流中 199 if (string.IsNullOrEmpty(id)) 200 { 201 return ResponseMessage(new HttpResponseMessage(HttpStatusCode.Gone)); 202 } 203 Tuple<string, Stream> tuple = GetAllAnnotationStream(id); 204 if (tuple.Item1 == "未能獲取到位元組流") 205 { 206 return ResponseMessage(new HttpResponseMessage(HttpStatusCode.Gone)); 207 } 208 responseMessage.Content = new StreamContent(tuple.Item2); 209 responseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); 210 HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment;filename=" + HttpUtility.UrlPathEncode(tuple.Item1)); 211 } 212 catch (Exception ex) 213 { 214 return ResponseMessage(new HttpResponseMessage() 215 { 216 Content = 217 new StringContent(JsonConvert.SerializeObject(Json(new { code = 0, msg = $"下載失敗-{ex.Message}" })), 218 Encoding.GetEncoding("UTF-8"), "application/json"), 219 StatusCode = HttpStatusCode.NoContent 220 }); 221 } 222 return ResponseMessage(responseMessage); 223 } 224 225 #endregion 根據實體id獲取實體資料下相關所有附件,打包成zip檔案下載 end 226 227 #region API專用 刪除附件 228 /// <summary> 229 /// 刪除附件 230 /// </summary> 231 /// <param name="id">附件ids</param> 232 /// <returns></returns> 233 [Route("DelAnnotation")] 234 [HttpPost] 235 public object DelAnnotation([FromBody] string id) 236 { 237 try 238 { 239 if (string.IsNullOrEmpty(id)) 240 { 241 return Json(new { code = 0, msg = $"刪除附件時id不能為空" }); 242 } 243 DeleteAnnotation(id); 244 return Json(new { code = 1, msg = "刪除成功" }); ; 245 } 246 catch (Exception ex) 247 { 248 return Json(new { code = 0, msg = $"刪除失敗:{ex.Message}" }); 249 } 250 } 251 #endregion API專用 刪除附件 252 253 #region 內部方法 start 254 255 #region 上傳附件存到Annotation中 start 256 /// <summary> 257 /// 將上傳檔案物件轉化成byte位元組流 258 /// </summary> 259 /// <param name="httpPostedFile">上傳檔案</param> 260 /// <returns></returns> 261 private byte[] GetFileByte(HttpPostedFile httpPostedFile) 262 { 263 byte[] bytes = new byte[httpPostedFile.ContentLength]; 264 using (BinaryReader reader = new BinaryReader(httpPostedFile.InputStream, Encoding.UTF8)) 265 { 266 bytes = reader.ReadBytes(bytes.Length); 267 } 268 return bytes; 269 } 270 /// <summary> 271 /// 建立附件資料 272 /// </summary> 273 /// <param name="annotation"></param> 274 /// <returns></returns> 275 private Guid CreateAnnotation(AnnotationModel annotation) 276 { 277 Guid guid = Guid.Empty; 278 IOrganizationService orgService = OrgServiceUtil.Client; 279 Entity entity = new Entity("annotation"); 280 entity["filename"] = annotation.FileName; 281 entity["subject"] = annotation.Subject; 282 entity["mimetype"] = annotation.MimeType; 283 entity["filesize"] = annotation.FileSize; 284 entity["documentbody"] = annotation.DocumentBody; 285 entity["objectid"] = new EntityReference(annotation.ObjectIdName, annotation.ObjectId); 286 guid = orgService.Create(entity); 287 return guid; 288 } 289 #endregion 上傳附件存到Annotation中 end 290 291 292 #region 根據附件id獲取附件位元組流 start 293 /// <summary> 294 /// 根據附件id獲取附件位元組流 295 /// </summary> 296 /// <param name="annotationid">annotationid</param> 297 /// <returns></returns> 298 public Tuple<string, Stream> GetAnnotationStream(string annotationid) 299 { 300 Tuple<string, Stream> rr = new Tuple<string, Stream>("未能獲取到位元組流", null); 301 try 302 { 303 IOrganizationService orgService = OrgServiceUtil.Client; 304 ColumnSet cols = new ColumnSet("filename", "documentbody"); 305 Entity entity = orgService.Retrieve("annotation", new Guid(annotationid), cols); 306 string documentbody = entity.GetAttributeValue<string>("documentbody"); 307 string filename = entity.GetAttributeValue<string>("filename"); 308 byte[] fileContent = Convert.FromBase64String(documentbody); 309 Stream fileStream = new MemoryStream(fileContent); 310 rr = new Tuple<string, Stream>(filename, fileStream); 311 } 312 catch (Exception) 313 { 314 return rr; 315 } 316 return rr; 317 } 318 #endregion 根據附件id獲取附件位元組流 end 319 320 #region 根據實體資料id獲取所有附件並打包成zip檔案,然後轉換成位元組流 start 321 /// <summary> 322 /// 根據實體資料id獲取所有附件達成zip包後轉換成位元組流 323 /// </summary> 324 /// <param name="entityId">實體資料id</param> 325 /// <returns></returns> 326 public Tuple<string, Stream> GetAllAnnotationStream(string entityId) 327 { 328 Tuple<string, Stream> rr = new Tuple<string, Stream>("未能獲取到位元組流", null); 329 try 330 { 331 IOrganizationService orgService = OrgServiceUtil.Client; 332 //1.根據實體資料id查詢出所有相關得附件 333 var dbList = GetDbFileByEntityId(orgService,new Guid(entityId)); 334 //2.遍歷所有相關得附件 將各個附件流轉化存放到記憶體字典中 335 Dictionary<string, byte[]> dic = new Dictionary<string, byte[]>(); 336 foreach (var cc in dbList) 337 { 338 string documentbody = cc.GetAttributeValue<string>("documentbody"); 339 string filename = cc.GetAttributeValue<string>("filename"); 340 byte[] fileContent = Convert.FromBase64String(documentbody); 341 Tuple<string, byte[]> tt = new Tuple<string, byte[]>(filename, fileContent); 342 if (tt.Item2 == null) 343 { 344 continue; 345 } 346 dic.Add(tt.Item1, tt.Item2); 347 } 348 if (dic.Count > 0) 349 { 350 //3.將多個位元組流從字典壓縮成zip檔案並轉成一個位元組流 351 Stream stream = ZipByteHelper.SetbytesToZipStream2(dic); 352 rr = new Tuple<string, Stream>("附件.zip", stream); 353 } 354 } 355 catch (Exception) 356 { 357 return rr; 358 } 359 return rr; 360 } 361 362 private List<Entity> GetDbFileByEntityId(IOrganizationService orgService,Guid entityId) 363 { 364 var list = new List<Entity>(); 365 QueryExpression query = new QueryExpression("annotation"); 366 query.ColumnSet = new ColumnSet(true); 367 query.Criteria.AddCondition(new ConditionExpression("objectid", ConditionOperator.Equal, entityId)); 368 var datas = orgService.RetrieveMultiple(query); 369 list = datas.Entities.ToArray().ToList(); 370 return list; 371 } 372 #endregion 根據實體資料id獲取所有附件並打包成zip檔案,然後轉化稱位元組流 end 373 374 #region 刪除附件 start 375 /// <summary> 376 /// 刪除附件註釋 377 /// </summary> 378 /// <param name="annotationid">附件id</param> 379 /// <returns></returns> 380 /// <exception cref="Exception"></exception> 381 public bool DeleteAnnotation(string annotationid) 382 { 383 bool result = true; 384 if (string.IsNullOrEmpty(annotationid)) 385 { 386 result = false; 387 } 388 IOrganizationService orgService = OrgServiceUtil.Client; 389 orgService.Delete("annotation", Guid.Parse(annotationid)); 390 return result; 391 } 392 #endregion 刪除附件 end 393 #endregion 內部方法 end 394 } 395 }
擴充Zip流壓縮幫助類
1 using System; 2 using System.Collections.Generic; 3 using System.IO; 4 using System.Linq; 5 using System.Web; 6 7 namespace ZZ_Common_API.Utilities 8 { 9 /// <summary> 10 /// 壓縮、解壓幫助類 11 /// </summary> 12 public static class ZipByteHelper 13 { 14 /// <summary> 15 /// 將多個檔案流轉化成zip檔案流 16 /// </summary> 17 /// <param name="dic"></param> 18 /// <returns></returns> 19 public static Stream SetbytesToZipStream2(Dictionary<string, byte[]> dic) 20 { 21 byte[] buffer = new byte[6500]; 22 MemoryStream returnStream = new MemoryStream(); 23 var zipMs = new MemoryStream(); 24 using (ICSharpCode.SharpZipLib.Zip.ZipOutputStream zipStream = new ICSharpCode.SharpZipLib.Zip.ZipOutputStream(zipMs)) 25 { 26 zipStream.SetLevel(9);//設定 壓縮等級 (9級 500KB 壓縮成了96KB) 27 foreach (var kv in dic) 28 { 29 string fileName = kv.Key; 30 using (var streamInput = new MemoryStream(kv.Value)) 31 { 32 zipStream.PutNextEntry(new ICSharpCode.SharpZipLib.Zip.ZipEntry(fileName)); 33 while (true) 34 { 35 var readCount = streamInput.Read(buffer, 0, buffer.Length); 36 if (readCount > 0) 37 { 38 zipStream.Write(buffer, 0, readCount); 39 } 40 else 41 { 42 break; 43 } 44 } 45 zipStream.Flush(); 46 } 47 } 48 zipStream.Finish(); 49 zipMs.Position = 0; 50 zipMs.CopyTo(returnStream, 5600); 51 } 52 returnStream.Position = 0; 53 54 return returnStream; 55 } 56 } 57 }