Nuget服务的搭建-打包-上传-删除

为了便于公共库的内部复用,我们可以架设自己的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>
编译发布后的结果,参看下面的图

首面界面

上传界面


查看与删除包的界面

源码下载

转载请注明出处


原文地址:https://www.cnblogs.com/sparkleDai/p/7604901.html