为了便于公共库的内部复用,我们可以架设自己的nuget服务。首先创建一个Asp.net的空项目NugetServer,其次使用nuget安装nuget.server包,如下图
添加了nuget.server后,直接编译然后发布到IIS下,一个简单的nugetServer就搭建成功了。但是为了能更方便的操作,还需要加一些简单的功能,比如nuget包的上传和删除。
在上传nuget包之前,还要知道nuget包的打包过程,需要用到打包工具NuGetPackageExplorer.application,点击下载即可。下面是打包的过程,按图顺序。
打包后保存,生成了xxx.nuget文件直接复制到nuget服务的Packages文件夹下,参看下图
之后在VS的nuget设置中,添加nuget服务的程序包源,参看下图
之后在项目中搜索相应的包然后引用,参看下图
选择相应的版本安装即可。
在上面的过程中,nuget包打好后,是直接复制到服务器中的,但为了便于管理和操作,我们做了一个简单的上传和删除的页面来进行管理。
上传的前端代码
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>上传</title> <meta charset="utf-8" /> <link href="Stylesheets/style.css" rel="stylesheet" /> <script src="Scripts/zepto.min.js"></script> </head> <body> <div id="ajaxWait" class="mask"> <img src="Images/loading.gif" /> </div> <div class="body"> <div class="upload-container"> <div> <div class="upload-wrap"> <input type="file" id="filePackage" class="upload-pic" value="上传"> <span> 选择上传的包 </span> </div> </div> <div><span id="filePackageInfo"></span></div> </div> <div> <input type="button" value="上传" id="btnUpload" class="upload-wrap " /> </div> </div> </body> </html> <script type="text/javascript"> $(document).ready(function () { $("#btnUpload").click(function () { $("#ajaxWait").show(); var formData = new FormData(); formData.append("filePackage", document.getElementById("filePackage").files[0]); $.ajax({ url: "/api/Package/Upload", type: "POST", data: formData, contentType: false,//必须false才会自动加上正确的Content-Type processData: false,//必须false才会避开jQuery对 formdata 的默认处理.XMLHttpRequest会对 formdata 进行正确的处理. success: function (data) { $("#ajaxWait").hide(); if (data.Status == 0) { alert("上传成功!"); } else { alert(data.Message); } }, error: function (data) { $("#ajaxWait").hide(); alert("上传失败!" + data.Message); } }); }); function fileInputChang(obj) { var id = obj.target.id; var files = document.getElementById(id).files; if (!files || files.length <= 0) { img.src = ""; return; } var file; if (files && files.length > 0) { // 获取目前上传的文件 file = files[0]; var size = ''; if (file.size > 1024 * 1024) { size = (file.size / (1024 * 1024)).toFixed(2) + 'M'; } else { size = (file.size / 1024).toFixed(2) + 'K'; } var name = file.name; document.getElementById("filePackageInfo").innerHTML = name + " " + size; } } $("#filePackage").change(fileInputChang); }); </script>
删除的前端代码
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>查看/删除</title> <meta charset="utf-8" /> <link href="Stylesheets/style.css" rel="stylesheet" /> <script src="Scripts/zepto.min.js"></script> </head> <body> <div class="all"> <div id="ajaxWait" class="mask"> <img src="images/loading.gif" /> </div> <div class="head"> <h1>包列表</h1> </div> <div class="body"> <div> <table> <tbody id="packageContainer"></tbody> </table> </div> <div> <input type="button" value="删除" id="btnDelete" class="btn" /> </div> </div> <div class="footer"></div> </div> </body> </html> <script type="text/javascript"> var g_packageList = {}; function generateIndexListHtml() { var list = g_packageList; var html = ""; var tr = ""; var idFlag = ""; for (var i = 0; i < list.length; i++) { if (i != 0 && i % 6 == 0) { tr += "</tr>"; html += tr; tr = ""; } if (tr.length <= 0) { tr += "<tr>"; } var packageItem = list[i]; tr += '<td ><input type="checkbox" id="checkbox_' + i + '"/></td> ' + '<td>' + packageItem.Name + '</td>' + '<td>' + packageItem.Version + '</td>'; if (i == list.length - 1) { tr += "</tr>"; html += tr; tr = ""; } } return html; } function loadList() { $("#ajaxWait").show(); $.ajax({ type: 'get', url: "/api/Package/List", data: {}, success: function (result) { try { if (result.Status != 0) { $("#ajaxWait").hide(); alert(result.Message); return; } g_packageList = result.Data; var container = $('#packageContainer'); var html = generateIndexListHtml(); container.html(html); $("#ajaxWait").hide(); } catch (e) { $("#ajaxWait").hide(); alert(e.message); } }, error: function (result) { $("#ajaxWait").hide(); alert("error"); } }); } $(document).ready(function () { loadList(); $("#btnDelete").click(function () { if (!confirm("确定要删除吗?")) { return; } var id = ""; var list = []; for (var i = 0; i < g_packageList.length; i++) { id = "checkbox_" + i; if ($("#" + id).prop("checked")) { list.push(g_packageList[i]); } } $("#ajaxWait").show(); $.ajax({ type: 'post', url: "/api/Package/Delete", data: JSON.stringify(list), contentType: "application/json", success: function (result) { try { $("#ajaxWait").hide(); if (result.Status != 0) { alert(result.Message); return; } loadList(); } catch (e) { $("#ajaxWait").hide(); alert(e.message); } }, error: function (result) { $("#ajaxWait").hide(); alert("error"); } }); }); }); </script>
上传和删除的后端代码(采用MVC的Controller来接收数据)
using System; using System.Collections.Generic; using System.Data; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using System.Web; using System.Web.Http; namespace NugetServer.Controllers { public class PackageController : ApiController { #region List [HttpGet] public BaseDataPackage<List<PackageItem>> List() { var result = new BaseDataPackage<List<PackageItem>>(); try { var packageItemList = new List<PackageItem>(); var physicsRoot = FetchPhysicsRootDir(); DirectoryInfo dirInfo = new DirectoryInfo(physicsRoot); var dirs = dirInfo.GetDirectories(); PackageItem packageItem = null; foreach (var dir in dirs) { var versionDirInfos = (new DirectoryInfo(dir.FullName)).GetDirectories(); foreach (var versionDirInfo in versionDirInfos) { packageItem = new PackageItem(); packageItem.Name = dir.Name; packageItem.Version = versionDirInfo.Name; packageItemList.Add(packageItem); } } result.Data = packageItemList; result.Message = "OK"; result.Status = StatusCode.OK; } catch (Exception ex) { result.Data = null; result.Message = ex.Message; result.Status = StatusCode.Fail; } return result; } #endregion #region Upload [HttpPost] public BaseDataPackage<string> Upload() { var result = new BaseDataPackage<string>(); if (!Request.Content.IsMimeMultipartContent()) { result.Status = StatusCode.Fail; result.Message = "Data Invalid"; result.Data = null; return result; } string physicsRoot = FetchPhysicsRootDir(); var provider = new RenameMultipartFormDataStreamProvider(physicsRoot); IEnumerable<HttpContent> parts = null; Task.Factory .StartNew(() => { parts = Request.Content.ReadAsMultipartAsync(provider).Result.Contents; }, CancellationToken.None, TaskCreationOptions.LongRunning, // guarantees separate thread TaskScheduler.Default) .Wait(); try { if (provider != null && provider.FileNameList != null && provider.FileNameList.Count > 0) {//刷新包缓存 RefreshPackageCache(); } result.Status = StatusCode.OK; result.Message = "OK"; result.Data = null; } catch (Exception ex) { result.Status = StatusCode.Fail; result.Message = ex.Message; result.Data = null; } return result; } #region RenameMultipartFormDataStreamProvider /// <summary> /// 重命名上传的文件 /// </summary> public class RenameMultipartFormDataStreamProvider : MultipartFormDataStreamProvider { #region 属性 /// <summary> /// 上传的文件名列表 /// </summary> public List<string> FileNameList { get; set; } = new List<string>(); #endregion public RenameMultipartFormDataStreamProvider(string root) : base(root) { } public override string GetLocalFileName(HttpContentHeaders headers) { //截取文件扩展名 string fileName = headers.ContentDisposition.FileName.TrimStart('"').TrimEnd('"'); if (!FileNameList.Contains(fileName)) { FileNameList.Add(fileName); } return fileName; } } #endregion #endregion #region FetchPhysicsRootDir private string FetchPhysicsRootDir() { var server = HttpContext.Current.Server; string webRoot = "~/Packages/"; string physicsRoot = server.MapPath(webRoot); if (!Directory.Exists(physicsRoot)) { Directory.CreateDirectory(physicsRoot); } return physicsRoot; } #endregion #region RefreshPackageCache /// <summary> /// 刷新包缓存 /// </summary> private void RefreshPackageCache() { var uri = HttpContext.Current.Request.Url; var port = uri.Port; var clearCacheApiUrl = VirtualPathUtility.ToAbsolute("~/nugetserver/api/clear-cache");//nuget服务的清除缓存API clearCacheApiUrl = "http://localhost:" + port.ToString() + clearCacheApiUrl; WebRequest request = WebRequest.Create(clearCacheApiUrl); request.Method = "GET"; WebResponse response = request.GetResponse(); Stream stream = response.GetResponseStream(); Encoding encode = Encoding.UTF8; StreamReader reader = new StreamReader(stream, encode); string resultJson = reader.ReadToEnd(); } #endregion #region Delete [HttpPost] public BaseDataPackage<string> Delete([FromBody] List<PackageItem> list) { var result = new BaseDataPackage<string>(); if (list == null || list.Count <= 0) { result.Status = StatusCode.OK; result.Message = "删除列表为空"; return result; } try { var physicsRoot = FetchPhysicsRootDir(); var temp = ""; foreach (var packageItem in list) { temp = physicsRoot + $"{packageItem.Name}\{packageItem.Version}"; DirectoryInfo di = new DirectoryInfo(temp); di.Delete(true); } if (list != null && list.Count > 0) { RefreshPackageCache(); } result.Status = StatusCode.OK; result.Message = "OK"; } catch (Exception ex) { result.Status = StatusCode.Fail; result.Message = ex.Message; } return result; } #endregion } #region PackageItem public class PackageItem { #region 属性 /// <summary> /// 包名 /// </summary> public string Name { get; set; } /// <summary> /// 版本 /// </summary> public string Version { get; set; } #endregion } #endregion }用到的CSS
.btn { 100px; height: 34px; color: #fff; letter-spacing: 1px; background: #3385ff; border-bottom: 1px solid #2d78f4; outline: medium; -webkit-appearance: none; -webkit-border-radius: 0; } .upload-wrap { position: relative; 158px; height: 43px; font-size: 16px; border: 1px solid #cacbcc; line-height: 43px; margin: 0 auto; color: #fff; text-align: center; background: #3385ff; border-bottom: 1px solid #2d78f4; float: left; } .upload-pic { position: absolute; font-size: 0; 100%; height: 100%; outline: 0; opacity: 0; filter: alpha(opacity=0); margin-left: -18px; z-index: 1; cursor: pointer; } .upload-container { height:80px; } .mask{ 100%; height: 100%; background: rgba(0, 0, 0, 0.8); position: fixed; top: 0; left: 0; z-index: 998; display: none; text-align:center; padding-top:10%; }相应的default.aspx页面也作了修改,代码如下
<%@ Page Language="C#" %> <%@ Import Namespace="NuGet.Server" %> <%@ Import Namespace="NuGet.Server.Infrastructure" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head id="Head1" runat="server"> <title>NuGet Private Repository</title> <style> body { font-family: Calibri; } </style> </head> <body> <div> <h2>NuGet.Server v<%= typeof(NuGet.Server.DataServices.ODataPackage).Assembly.GetName().Version %></h2> <fieldset style=" 800px"> <legend><strong>包管理</strong></legend> <p><a href="delete.html" target="_blank">查看nuget包</a> <a href="upload.html" target="_blank">上传nuget包</a> </p> <p> 在包管理器中添加 <strong><%= Helpers.GetRepositoryUrl(Request.Url, Request.ApplicationPath) %></strong>作为包源(参照下图),以便使用自己构建的nuget服务. <img src="Images/nuget_source_set.png" /> </p> </fieldset> <fieldset style=" 800px"> <legend><strong>nuget打包</strong></legend> <div> <a href="Kits/NuGetPackageExplorer.application">nuget打包工具下载</a> </div> <h3>打包工具使用说明</h3> <p> <img src="Images/nuget_1.png" /> <img src="Images/nuget_2.png" /> <img src="Images/nuget_3.png" /> </p> </fieldset> </div> </body> </html>编译发布后的结果,参看下面的图
首面界面
上传界面
查看与删除包的界面
转载请注明出处