Dynamics CRM中自定義頁面實現附件管理包含下載模板、上傳、下載、刪除

2023-10-26 18:01:28
前言
附件使用的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,不然後臺介面接收不到檔案。後臺介面將接受的檔案轉成流儲存到資料庫中。
其他的檔案下載和刪除就不介紹了,比較簡單。
 
效果圖
 
0
 
 
程式碼
 
自定義按鈕開啟對話方塊頁面
 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]["[email protected]"] + " </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>
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 }
webAPI介面
拓展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 }
ZIP幫助類