Asp.Net Core写个共享磁盘文件Web查看器
本篇和大家分享的是一个磁盘文件查看系统,严格来说是使用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查看器的更多相关文章
- 使用Http-Repl工具测试ASP.NET Core 2.2中的Web Api项目
今天,Visual Studio中没有内置工具来测试WEB API.使用浏览器,只能测试http GET请求.您需要使用Postman,SoapUI,Fiddler或Swagger等第三方工具来执行W ...
- 给 asp.net core 写一个简单的健康检查
给 asp.net core 写一个简单的健康检查 Intro 健康检查可以帮助我们知道应用的当前状态是不是处于良好状态,现在无论是 docker 还是 k8s 还是现在大多数的服务注册发现大多都提供 ...
- 给 asp.net core 写个中间件来记录接口耗时
给 asp.net core 写个中间件来记录接口耗时 Intro 写接口的难免会遇到别人说接口比较慢,到底慢多少,一个接口服务器处理究竟花了多长时间,如果能有具体的数字来记录每个接口耗时多少,别人再 ...
- ASP.NET Core中的jQuery Unobtrusive Ajax帮助器
最近在ASP.NET Core下写文章管理系统时,准备在分页显示文章内容时,使用Ajax.网上找了篇帖文,简单翻一下,仅供自己查阅. 原链接:https://dotnetthoughts.net/jq ...
- 【ASP.NET Core】体验一下 Mini Web API
在上一篇水文中,老周给大伙伴们简单演示了通过 Socket 编程的方式控制 MPD (在树莓派上).按照计划,老周还想给大伙伴们演示一下使用 Web API 来封装对 MPD 控制.思路很 Easy, ...
- 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 ...
- 使用静态基类方案让 ASP.NET Core 实现遵循 HATEOAS Restful Web API
Hypermedia As The Engine Of Application State (HATEOAS) HATEOAS(Hypermedia as the engine of applicat ...
- DataProtection设置问题引起不同ASP.NET Core站点无法共享用户验证Cookie
这是这两天ASP.NET Core迁移中遇到的一个问题.2个ASP.NET Core站点(对应于2个不同的ASP.NET Core Web应用程序),2个站点都可以登录,但在其中任1个站点登录后,在当 ...
- 在ASP.NET Core MVC中构建简单 Web Api
Getting Started 在 ASP.NET Core MVC 框架中,ASP.NET 团队为我们提供了一整套的用于构建一个 Web 中的各种部分所需的套件,那么有些时候我们只需要做一个简单的 ...
随机推荐
- VUE进阶(路由等)
初级教程:http://www.cnblogs.com/dmcl/p/6137469.html VUE进阶 自定义指令 http://cn.vuejs.org/v2/guide/custom-dire ...
- Elasticsearch搜索之explain评分分析
Lucene的IndexSearcher提供一个explain方法,能够解释Document的Score是怎么得来的,具体每一部分的得分都可以详细地打印出来.这里用一个中文实例来纯手工验算一遍Luce ...
- php 启动过程 - sapi MSHUTDOWN 过程
php 启动过程 - sapi MSHUTDOWN 过程 概述 当服务器关闭时, 会走到 sapi MSHUTDOWN 过程 注册过程 本次内容是在 php 启动过程 - sapi MINIT 过程 ...
- div模拟输入框input/textarea
//html<!--填写信息--> <div class="info-wrap"> <form class="formToCheck&quo ...
- 你想要的都在这里,ASP.NET Core MVC四种枚举绑定方式
前言 本节我们来讲讲在ASP.NET Core MVC又为我们提供了哪些方便,之前我们探讨过在ASP.NET MVC中下拉框绑定方式,这节我们来再来重点看看枚举绑定的方式,充分实现你所能想到的场景,满 ...
- 开始使用gentoo linux——gentoo安装笔记(下)
gentoo安装笔记(下) 上一章,已经对操作系统安装做了充分准备,并且已经从livecd(u盘系统)切换进入了gentoo安装环境中. 不过现在才是真正的开始!打起精神!这可不是在装ubuntu! ...
- 【翻译】FreeMarker——入门
原文传送门 1. Template + data-model = output data-model是一个树状模型,通常是一个java对象. 2.data-model 入门 hashes(散列):目录 ...
- stl map容器 学习
#include<map> 1.map的声明: map<string,int>map_1; map_1 就是一个string对int的映射. 2.map的用法(映射): map ...
- 微信公众号开发笔记2(nodejs)
本篇主要记录调用微信各种api和功能实现 一.始于access_token 无论调用微信的什么api,都需要一个查询参数,就是我们每隔1小时或者2小时获取的access_token,笔记1中已经保证了 ...
- linux服务器下安装node
在百度上搜了好久,都没有完整的答案,好多都已经过时了!特留下此脚印 # 检查是否已经安装pythonrpm -qa | grep python# 查版本python# 最好是重新安装 Python推荐 ...