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 {
width: 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;
width: 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;
width: 100%;
height: 100%;
outline: 0;
opacity: 0;
filter: alpha(opacity=0);
margin-left: -18px;
z-index: 1;
cursor: pointer;
}
.upload-container {
height:80px;
}
.mask{
width: 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="width: 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="width: 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>
编译发布后的结果,参看下面的图
首面界面
上传界面
查看与删除包的界面
转载请注明出处

浙公网安备 33010602011771号