本篇和大家分享的是一个磁盘文件查看系统,严格来说是使用NetCore写的一个Web系统应用,由于NetCore跨平台特性,我生成了exe的运行包,只需要配置运行电脑ip+端口,即可在浏览器中通过IP+端口的方式访问目标调用上的所有目录,不错是所有目录(如果您有:C,D,E,F盘都可以访问),当然为了安全最好限制下;还有上传,备份功能,具体看下面的分享内容吧;git地址:https://github.com/shenniubuxing3/ShenNiu.LogTool

查看器功能说明与演示

本查看器主要是为了方便大家查看服务器上的日志,这里没有考虑其他安全性问题,比如特定人员登录才能查看,这个需要您们自己去增加;如果你服务器有对外开放了ip,那么运行这个软件的时候建议考虑配置成您们公司内网的ip,这里可以避免一些安全性问题;下面是主要功能:

. 通过可以定义文件配置常用磁盘访问地址

. 查看磁盘目录下的文件夹和文件

. 部分可访问行文件(如:txt,DLL,图片等)可以在浏览器中打开或下载(访问性格式由程序配置)

. 上传多个文件到指定磁盘

. 文件备份(如果上传的文件已经存在,会自动备份到bak文件夹中)

效果gif图片,有点花多多包涵:

效果还可以吧,不妨“推荐”下;

磁盘列表功能

首先,要明确的是在NetCore1.1中api已经和大部分能和framwork对应上了(据@善友一篇博客简单介绍说NetCore2.0的api已经能够和framwork持平了),因此这里我们能够直接使用DirectoryInfo,来查看磁盘路径的文件夹和文件,所以就有了查看列表Action的代码:

  /// <summary>
/// 磁盘列表
/// </summary>
/// <param name="path">磁盘路径</param>
/// <returns></returns>
public IActionResult Index(string path)
{
Console.WriteLine($"IP:{HttpContext.Connection.RemoteIpAddress}正在查看磁盘:{path}");
var list = new List<FileSystemInfo>();
MoSearch moSerach = new MoSearch { Txt1 = path };
ViewData["Search"] = moSerach; if (string.IsNullOrWhiteSpace(path)) { return View(list); }
if (path.StartsWith("c:", StringComparison.OrdinalIgnoreCase)) { this.MsgBox($"无权限访问:{path}"); return View(list); }
if (!System.IO.Directory.Exists(path)) { this.MsgBox($"磁盘路径:{path}不存在!"); return View(list); }
DirectoryInfo dic = new DirectoryInfo(path);
list = dic.GetFileSystemInfos().OrderByDescending(b => b.LastWriteTime).ToList(); return View(list);
}

这里我默认限制了C盘,并且采用自带的文件对象FileSystemInfo来返回信息,仅仅只需要一段 dic.GetFileSystemInfos().OrderByDescending(b => b.LastWriteTime).ToList() 就能获取按照最新修改时间得到磁盘目录信息;对应的View布局如下:

 @using System.IO
@using ShenNiu.LogTool.Extension;
@using ShenNiu.LogTool.Controllers
@model List<FileSystemInfo>
@{
ViewData["Title"] = "日志搜索"; var moSearch = ViewData["Search"] as MoSearch;
}
<div>
<h4>@ViewData["Title"]</h4>
<hr />
<form id="form01" method="post" enctype="multipart/form-data">
<div class="form-group">
<label for="txt1">磁盘路径</label>
<input type="text" class="form-control" id="txt1" name="txt1" value="@moSearch.Txt1" style="max-width:100%" placeholder="会记录到后面的下拉框">
</div>
<div class="form-group">
<label for="sel1">常用地址</label>
<select name="sel1" id="sel1" class="form-control">
@*<option value="">==请选择==</option>
<optgroup label="日志">
<option value="D:\D\Joke">D:\D\Joke</option>
</optgroup>
<optgroup label="D盘">
<option value="D:\">D盘</option>
</optgroup>*@
</select> </div>
<div class="form-group ">
<input type="file" name="upFile" class="form-control" multiple placeholder="上传文件" />
</div>
<button type="button" id="btnSearch" class="btn btn-default">查 询</button>
<button type="button" id="btnUp" class="btn btn-default ">上 传</button>
<a href="javascript:window.history.go(-1);" class="btn btn-default">返 回</a>
<span id="span01" style="color:red">
@ViewData["msg"]
</span>
</form>
<hr />
<table class="table">
<thead>
<tr>
<th>文件名</th>
<th>磁盘路径</th>
<th>最后更新时间</th>
<th>创建时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>
@if (item.Attributes == FileAttributes.Archive)
{
<img src="/images/icon/@(item.Extension.GetExtensionIcon())" /><a href="/log/read?path=@item.FullName" target="_blank">@item.Name</a>
}
else if (item.Attributes == FileAttributes.Directory)
{
<img src="/images/icon/Directory1.jpg" /><a href="/log/index?path=@item.FullName">@item.Name</a>
}
else
{
<img src="/images/icon/@(item.Extension.GetExtensionIcon())" /><a href="/log/index?path=@item.FullName">@item.Name</a>
}
@item.Attributes
</td>
<td>@item.FullName</td>
<td>@item.LastWriteTime</td>
<td>@item.CreationTime</td>
<td>
@if (item.Attributes == FileAttributes.Archive)
{
<a href="/log/read?path=@item.FullName" target="_blank">查看</a>
}
</td>
</tr>
}
</tbody>
</table>
<div style="color:red">@ViewData["msg"]</div>
</div>
<script type="text/javascript">
$(function(){ $("#btnUp").on("click", function () {
var msg = $("#span01");
var form = document.getElementById("form01");
//console.log(form);
var data = new FormData(form); $.ajax({
type: "POST",
url: "/log/AjaxFileUp",
data: data, contentType: false,
processData: false,
success: function (data) {
if (data) {
msg.html(data.msg);
}
},
error: function () {
msg.html("上传文件异常,请稍后重试!");
}
});
}); $("#btnSearch").on("click",function(){ var sel1Val = $.trim($("select[name='sel1'] option:selected").val());
var txt1Val = $.trim($("#txt1").val()); var pathVal = sel1Val.length<=?txt1Val:sel1Val;
window.location.href="/log/index?path="+pathVal;
}); $.getJSON("/log/GetSelData",function(data){
console.log(data);
if(data){ var sel1 = $("select[name='sel1']");
var gArr = [];
gArr.push('<option value="">==请选择==</option>');
$.each(data,function(i,item){ gArr.push('<optgroup label="'+item.gname+'">'); $.each(item.gval,function(i2,item2){ gArr.push('<option value="'+item2.val+'">'+item2.name+'</option>');
}); gArr.push('</optgroup>');
}); sel1.html(gArr.join(''));
}
});
})
</script>

列表页面的常用地址来源有系统配置文件配置的,通过前端ajax调用接口获取配置的json内容,接口Action代码:

  public async Task<ContentResult> GetSelData()
{
var apiUrl = $"http://{Request.Host.Host}:{Request.Host.Port}/js/tooldata/logconf.json";
var str = string.Empty;
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(apiUrl);
str = await client.GetStringAsync(apiUrl);
}
return Content(str);
}

配置文件格式和内容如:

 [
{
"gname": "日志",
"gval": [
{
"name": "JokeLog",
"val": "D:\\D\\Joke"
}
]
},
{
"gname": "D盘",
"gval": [
{
"name": "D盘",
"val": "D:\\"
}
]
}
]

指定磁盘目录上传文件和自动备份

通常咋们有这样的情况,我们没有直接访问服务器的权限,想上传个东西很麻烦,每次只能通过运维(当然这是正规的流程),可是往往一些特殊情况不得不自己传递个东西发布,因此这里增加了上传功能,并且上传时候如果已存在相同文件,那么在覆盖之前会自动增加备份到tempbak中去;

 /// <summary>
/// 本查看系统具有上传文件的功能
/// </summary>
/// <returns></returns>
[HttpPost]
public async Task<JsonResult> AjaxFileUp()
{
var data = new MoData { Msg = "上传失败" };
try
{
var upPath = Request.Form["txt1"];
if (string.IsNullOrWhiteSpace(upPath)) { data.Msg = "请在【磁盘路径】输入框输入上传路径。"; return Json(data); }
if (!System.IO.Directory.Exists(upPath)) { data.Msg = $"磁盘路径:{upPath}不存在!"; return Json(data); }
upPath = upPath.ToString().TrimEnd('\\'); var files = Request.Form.Files.Where(b => b.Name == "upFile");
//非空限制
if (files == null || files.Count() <= ) { data.Msg = "请选择上传的文件。"; return Json(data); } //格式限制
//var allowType = new string[] { "image/jpeg", "image/png" };
//if (files.Any(b => !allowType.Contains(b.ContentType)))
//{
// data.Msg = $"只能上传{string.Join(",", allowType)}格式的文件。";
// return Json(data);
//} //大小限制
var nMax = ;
if (files.Sum(b => b.Length) >= * * nMax)
{
data.Msg = $"上传文件的总大小只能在{nMax}M以下。"; return Json(data);
} //删除过去备份的文件
var basePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "tempbak");
DirectoryInfo dic = new DirectoryInfo(basePath);
var nCount = dic.GetFiles().Count();
var nMaxCount = ;
if (nCount > nMaxCount) //大于nMaxCount个文件清空临时目录
{
foreach (var item in dic.GetFiles().OrderBy(b => b.LastWriteTime).Take(nCount - nMaxCount))
{
try
{
item.Delete();
}
catch (Exception ex) { }
}
} //写入服务器磁盘
var upLog = new StringBuilder(string.Empty);
foreach (var file in files)
{ var fileName = file.FileName;
var path = Path.Combine(upPath, fileName);
upLog.AppendFormat("文件:{0};", path); //存在文件需要备份
if (System.IO.File.Exists(path))
{
FileInfo info = new FileInfo(path);
var tempPath = Path.Combine(basePath, info.Name); //备份目录
var newInfo = info.CopyTo(tempPath, true);
if (newInfo == null) { upLog.Append($"备份:失败,请稍后重试!"); }
else { upLog.Append($"备份:成功!"); }
} using (var stream = System.IO.File.Create(path))
{
await file.CopyToAsync(stream);
}
upLog.Append($"上传:成功;<br/>");
}
data.Msg = upLog.ToString();
data.Status = ;
}
catch (Exception ex)
{
data.Msg += ex.Message;
}
Console.WriteLine($"IP:{HttpContext.Connection.RemoteIpAddress}正在上传:{data.Msg}");
return Json(data);
}

关键点的逻辑代码已经有注释了这里就不多说了,主要满足咋们的业务:上传+备份;至于上传的js代码已经在上面的列表试图中了这里就不重复贴出来了;这里用到了几个自定义实体类:

 /// <summary>
/// 接口统一类
/// </summary>
public class MoData
{
public string Msg { get; set; } public int Status { get; set; }
} /// <summary>
/// 搜索类
/// </summary>
public class MoSearch
{
public string Txt1 { get; set; } public string Sel1 { get; set; }
} /// <summary>
/// 文件
/// </summary>
public class MoFile
{
public string Name { get; set; }
public string Path { get; set; }
public string Url { get; set; }
public string Content { get; set; }
public FileAttributes Attributes { get; set; }
}

直接查看内容

该系统可以直接查看如:txt,log等后缀的文件,因为这种类型的文件一般都有读,写同时操作的情况,所以这里我采用的方式是先拷贝当前访问的文件到temp临时目录中,然后在读取内容或下载文件;当满足超过10个文件的设置,那么自动删除修改时间最小的文件,避免拷贝文件一直增多导致磁盘空间的成本;下面是读取Action的内容:

 /// <summary>
/// 查看内容
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public async Task<IActionResult> Read(string path)
{
Console.WriteLine($"IP:{HttpContext.Connection.RemoteIpAddress}正在查看文件:{path}"); var moFile = new MoFile { Path = path };
if (string.IsNullOrWhiteSpace(path)) { this.MsgBox($"文件路径:{path}不存在。"); return View(moFile); }
if (!System.IO.File.Exists(path)) { this.MsgBox($"文件路径:{path}不存在!"); return View(moFile); } try
{
FileInfo info = new FileInfo(path);
//if (!ExtensionClass._AllowExtension.Any(b => b.ToUpper() == info.Extension.ToUpper()))
//{
// this.MsgBox($"无法访问{info.Extension}的文件"); return View(moFile);
// } var basePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "temp");
DirectoryInfo dic = new DirectoryInfo(basePath);
var nCount = dic.GetFiles().Count();
var nMaxCount = ;
if (nCount > nMaxCount) //大于nMaxCount个文件清空临时目录
{
foreach (var item in dic.GetFiles().OrderBy(b => b.LastWriteTime).Take(nCount - nMaxCount))
{
try
{
item.Delete();
}
catch (Exception ex) { }
}
} var tempPath = Path.Combine(basePath, info.Name);
var newInfo = info.CopyTo(tempPath, true);
if (newInfo == null) { this.MsgBox($"文件:{path}查看失败,请稍后重试!"); return View(moFile); } moFile.Name = newInfo.Name;
moFile.Url = $"/{moFile.Name}";
moFile.Attributes = newInfo.Attributes;
if (moFile.Attributes == FileAttributes.Archive && !ExtensionClass._FileExtension.Any(b => b == newInfo.Extension))
{
using (var stream = newInfo.OpenRead())
{
using (var reader = new StreamReader(stream))
{
moFile.Content = await reader.ReadToEndAsync();
}
}
}
}
catch (Exception ex)
{
this.MsgBox($"文件:{path}查看失败,请稍后重试!");
}
return View(moFile);
}

怎么使用ShenNiu.LogTool工具呢

我这里只提供了一个windows x64平台的运行exe包ShenNiu.LogTool(不用安装什么运行环境),只需要双击“ShenNiu.LogTool.exe”-》配置Ip+端口(默认IP:127.0.0.1,端口:12345):

-》浏览器中输入:http://127.0.0.1:12345/Log 即可访问查看系统,剩下的操作就如上gif截图了;

使用nssm工具把NetCore生成的exe转成windows服务

本篇到这里还要讲一个工具nssm(这里不提供下载地址,个位网搜吧),因为就windows平台而言netcore生成如果不用iis发布,那么大多数都是通过exe来运行的,但是我们不可能再服务器上开很多个黑屏cmd一样的窗体,那这样服务器每次关闭的话那就用不了服务了;因此我们使用nssm把这个netcore上传的exe转成windows服务中去,这样就算关机重启也能及时启动;

由于windows服务不会提示让咋们输入绑定的ip,端口,所以这里我们需要改改代码:

 public static void Main(string[] args)
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
Console.OutputEncoding = Encoding.GetEncoding("GB2312"); //Console.WriteLine("输入服务绑定的Ip:");
//var strHost = Console.ReadLine(); if (string.IsNullOrWhiteSpace(strHost)) { strHost = "127.0.0.1"; }
//Console.WriteLine("输入服务绑定的端口:");
//var strPort = Console.ReadLine(); if (string.IsNullOrWhiteSpace(strPort)) { strPort = "12345"; } //var hostAndPort = $"http://{strHost}:{strPort}";
var hostAndPort = "http://127.0.0.1:12345"; var host = new WebHostBuilder()
.UseKestrel()
.UseUrls(hostAndPort)
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.UseApplicationInsights()
.Build(); host.Run();
}

然后利用nssm工具,首先通过cmd命令执行如下命令:

执行后会弹出一个框,然后如图操作:

再点击“install server”,不出意外的话会弹出一个 successful的提示;再来咋们看看windows服务中我们注册的服务:

这个时候该服务是未启动状态,所以我们可以直接通过操作界面启动下(当然也可以通过nssm命令启动),能正常启动没问题的话,我们就可以在浏览器中访问:http://127.0.0.1:12345/Log:

好了本章到此就结束了,怎么样干货还是可以吧,不妨点个"推荐",谢谢。再发下git地址:https://github.com/shenniubuxing3/ShenNiu.LogTool

Asp.Net Core写个共享磁盘文件Web查看器的更多相关文章

  1. 使用Http-Repl工具测试ASP.NET Core 2.2中的Web Api项目

    今天,Visual Studio中没有内置工具来测试WEB API.使用浏览器,只能测试http GET请求.您需要使用Postman,SoapUI,Fiddler或Swagger等第三方工具来执行W ...

  2. 给 asp.net core 写一个简单的健康检查

    给 asp.net core 写一个简单的健康检查 Intro 健康检查可以帮助我们知道应用的当前状态是不是处于良好状态,现在无论是 docker 还是 k8s 还是现在大多数的服务注册发现大多都提供 ...

  3. 给 asp.net core 写个中间件来记录接口耗时

    给 asp.net core 写个中间件来记录接口耗时 Intro 写接口的难免会遇到别人说接口比较慢,到底慢多少,一个接口服务器处理究竟花了多长时间,如果能有具体的数字来记录每个接口耗时多少,别人再 ...

  4. ASP.NET Core中的jQuery Unobtrusive Ajax帮助器

    最近在ASP.NET Core下写文章管理系统时,准备在分页显示文章内容时,使用Ajax.网上找了篇帖文,简单翻一下,仅供自己查阅. 原链接:https://dotnetthoughts.net/jq ...

  5. 【ASP.NET Core】体验一下 Mini Web API

    在上一篇水文中,老周给大伙伴们简单演示了通过 Socket 编程的方式控制 MPD (在树莓派上).按照计划,老周还想给大伙伴们演示一下使用 Web API 来封装对 MPD 控制.思路很 Easy, ...

  6. ASP.NET Core 中文文档 第二章 指南(2)用 Visual Studio 和 ASP.NET Core MVC 创建首个 Web API

    原文:Building Your First Web API with ASP.NET Core MVC and Visual Studio 作者:Mike Wasson 和 Rick Anderso ...

  7. 使用静态基类方案让 ASP.NET Core 实现遵循 HATEOAS Restful Web API

    Hypermedia As The Engine Of Application State (HATEOAS) HATEOAS(Hypermedia as the engine of applicat ...

  8. DataProtection设置问题引起不同ASP.NET Core站点无法共享用户验证Cookie

    这是这两天ASP.NET Core迁移中遇到的一个问题.2个ASP.NET Core站点(对应于2个不同的ASP.NET Core Web应用程序),2个站点都可以登录,但在其中任1个站点登录后,在当 ...

  9. 在ASP.NET Core MVC中构建简单 Web Api

    Getting Started 在 ASP.NET Core MVC 框架中,ASP.NET 团队为我们提供了一整套的用于构建一个 Web 中的各种部分所需的套件,那么有些时候我们只需要做一个简单的 ...

随机推荐

  1. VUE进阶(路由等)

    初级教程:http://www.cnblogs.com/dmcl/p/6137469.html VUE进阶 自定义指令 http://cn.vuejs.org/v2/guide/custom-dire ...

  2. Elasticsearch搜索之explain评分分析

    Lucene的IndexSearcher提供一个explain方法,能够解释Document的Score是怎么得来的,具体每一部分的得分都可以详细地打印出来.这里用一个中文实例来纯手工验算一遍Luce ...

  3. php 启动过程 - sapi MSHUTDOWN 过程

    php 启动过程 - sapi MSHUTDOWN 过程 概述 当服务器关闭时, 会走到 sapi MSHUTDOWN 过程 注册过程 本次内容是在 php 启动过程 - sapi MINIT 过程 ...

  4. div模拟输入框input/textarea

    //html<!--填写信息--> <div class="info-wrap"> <form class="formToCheck&quo ...

  5. 你想要的都在这里,ASP.NET Core MVC四种枚举绑定方式

    前言 本节我们来讲讲在ASP.NET Core MVC又为我们提供了哪些方便,之前我们探讨过在ASP.NET MVC中下拉框绑定方式,这节我们来再来重点看看枚举绑定的方式,充分实现你所能想到的场景,满 ...

  6. 开始使用gentoo linux——gentoo安装笔记(下)

    gentoo安装笔记(下) 上一章,已经对操作系统安装做了充分准备,并且已经从livecd(u盘系统)切换进入了gentoo安装环境中. 不过现在才是真正的开始!打起精神!这可不是在装ubuntu! ...

  7. 【翻译】FreeMarker——入门

    原文传送门 1. Template + data-model = output data-model是一个树状模型,通常是一个java对象. 2.data-model 入门 hashes(散列):目录 ...

  8. stl map容器 学习

    #include<map> 1.map的声明: map<string,int>map_1; map_1 就是一个string对int的映射. 2.map的用法(映射): map ...

  9. 微信公众号开发笔记2(nodejs)

    本篇主要记录调用微信各种api和功能实现 一.始于access_token 无论调用微信的什么api,都需要一个查询参数,就是我们每隔1小时或者2小时获取的access_token,笔记1中已经保证了 ...

  10. linux服务器下安装node

    在百度上搜了好久,都没有完整的答案,好多都已经过时了!特留下此脚印 # 检查是否已经安装pythonrpm -qa | grep python# 查版本python# 最好是重新安装 Python推荐 ...