吉特日化MES系统--通过浏览器调用标签打印
三年来做制造行业,差不多做了近30个工厂,也由纯软件转入到了大量的硬件对接,包含厂房设计(这个目前还只是小菜鸟),硬件设计(只是提提意见),安装实施调试(软件和硬件撕逼操作),当然面向的对象也由计算机转向了和各种人员的对接,一直想做一个牛逼的技术人员,可是天不如人愿只能游走于各种琐事中,也很少写技术相关的文章了,今天分享一个小小的应用点《打印标签》
一 标签打印
在制造行业中,在仓库物流行业中标签打印是非常常用的需求,标签除了标识物体之外,我们还有一个比较重要的应用 就是在标签上附上 条码 或 二维码, 特别是二维码目前在我们生活中无处不在。 而标签中使用 条码或二维码,其实就是为了加快物体的识别效率。 当然市面上有很多打印的标签软件,各个打印机厂商也都有自带打印软件,而打印机厂商自带的软件大部分都是CS结构的程序,而且无法和BS网页交互。、
以上标签在生产制造行业中比较常见,除了以上在原料,生产过程中使用,还有成品仓库包装,物流发货等,当然还有一些特殊的情况比如 包装外箱等使用喷码机来喷码的情况,这种并不是使用打印来打印的。标签的应用很多,但是有一点不同的就是标签纸张规格大小的不一样,以及经常变动的表现形式。
二 为什么不使用浏览器打印
浏览器也是带有打印功能的,一般比较常用使用浏览器打印都是打印A4,A5 等这种比较规整的纸张,当然浏览器也能够打印各种小标签。但是浏览器打印有一个不好的就是清晰度不够,至于为什么清晰度不够这个我也说不清楚,然后在连续打印等方面都比较弱,可能很多人会问我们也经常见到连续打印的,一般这种打印都是使用浏览器插件或者和打印客户端相连接的,这也是在《吉特日化MES系统》中应用的方式。
另外目前市面上见到的浏览器打印客户端大部分都是收费的,虽然很多费用还不低,另外就是绑架销售,比如很多打印都是集成在报表组件里面的,目前的报表组件随随便便一个授权就是几十万,这也是客户无法接受的,我们自己做项目也无法接受。
三 自己开发的打印组件
其实算不上自己开发的打印组件,只是基于.NET 的documentprint 打印类做了一定的封装,在之间的文字中有分享过。具体可以看看如下文章
当然也还有一些其他的打印案例的分享,有兴趣的可以翻阅一下以前的文章。
关于自己的打印组件源代码如下: https://github.com/hechenqingyuan/gitprint
四 用网页打印怎么办
上位系统的开发使用BS架构,这也就必然遇到了打印头疼的地方,如何打印标签。其实思路很简单,那就是网页出发打印指令,然后发送到客户单软件来打印,这是我这边的一个基本思路。
如图是一个打印标签的操作界面,就目前而言操作还是相对比较方便了,要想达到如上效果要解决如下几个问题:
(1) 读取本地连接的打印机(客户端获取)


public class DBPaperSize
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal struct structPrinterDefaults
{
[MarshalAs(UnmanagedType.LPTStr)]
public String pDatatype;
public IntPtr pDevMode;
[MarshalAs(UnmanagedType.I4)]
public int DesiredAccess;
}; [DllImport("winspool.Drv", EntryPoint = "OpenPrinter", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = false, CallingConvention = CallingConvention.StdCall), SuppressUnmanagedCodeSecurityAttribute()]
internal static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPTStr)]string printerName, out IntPtr phPrinter, ref structPrinterDefaults pd); [DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = false, CallingConvention = CallingConvention.StdCall), SuppressUnmanagedCodeSecurityAttribute()]
internal static extern bool ClosePrinter(IntPtr phPrinter); [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal struct structSize
{
public Int32 width;
public Int32 height;
} [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal struct structRect
{
public Int32 left;
public Int32 top;
public Int32 right;
public Int32 bottom;
} [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal struct structFormInfo1
{
[MarshalAs(UnmanagedType.I4)]
public int Flags;
[MarshalAs(UnmanagedType.LPTStr)]
public String pName;
public structSize Size;
public structRect ImageableArea;
}; [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal struct structDevMode
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public String dmDeviceName;
[MarshalAs(UnmanagedType.U2)]
public short dmSpecVersion;
[MarshalAs(UnmanagedType.U2)]
public short dmDriverVersion;
[MarshalAs(UnmanagedType.U2)]
public short dmSize;
[MarshalAs(UnmanagedType.U2)]
public short dmDriverExtra;
[MarshalAs(UnmanagedType.U4)]
public int dmFields;
[MarshalAs(UnmanagedType.I2)]
public short dmOrientation;
[MarshalAs(UnmanagedType.I2)]
public short dmPaperSize;
[MarshalAs(UnmanagedType.I2)]
public short dmPaperLength;
[MarshalAs(UnmanagedType.I2)]
public short dmPaperWidth;
[MarshalAs(UnmanagedType.I2)]
public short dmScale;
[MarshalAs(UnmanagedType.I2)]
public short dmCopies;
[MarshalAs(UnmanagedType.I2)]
public short dmDefaultSource;
[MarshalAs(UnmanagedType.I2)]
public short dmPrintQuality;
[MarshalAs(UnmanagedType.I2)]
public short dmColor;
[MarshalAs(UnmanagedType.I2)]
public short dmDuplex;
[MarshalAs(UnmanagedType.I2)]
public short dmYResolution;
[MarshalAs(UnmanagedType.I2)]
public short dmTTOption;
[MarshalAs(UnmanagedType.I2)]
public short dmCollate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public String dmFormName;
[MarshalAs(UnmanagedType.U2)]
public short dmLogPixels;
[MarshalAs(UnmanagedType.U4)]
public int dmBitsPerPel;
[MarshalAs(UnmanagedType.U4)]
public int dmPelsWidth;
[MarshalAs(UnmanagedType.U4)]
public int dmPelsHeight;
[MarshalAs(UnmanagedType.U4)]
public int dmNup;
[MarshalAs(UnmanagedType.U4)]
public int dmDisplayFrequency;
[MarshalAs(UnmanagedType.U4)]
public int dmICMMethod;
[MarshalAs(UnmanagedType.U4)]
public int dmICMIntent;
[MarshalAs(UnmanagedType.U4)]
public int dmMediaType;
[MarshalAs(UnmanagedType.U4)]
public int dmDitherType;
[MarshalAs(UnmanagedType.U4)]
public int dmReserved1;
[MarshalAs(UnmanagedType.U4)]
public int dmReserved2;
} [DllImport("winspool.Drv", EntryPoint = "AddForm", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = false, CallingConvention = CallingConvention.StdCall), SuppressUnmanagedCodeSecurityAttribute()]
internal static extern bool AddForm(IntPtr phPrinter, [MarshalAs(UnmanagedType.I4)] int level, ref structFormInfo1 form);
[DllImport("winspool.Drv", EntryPoint = "DeleteForm", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = false, CallingConvention = CallingConvention.StdCall), SuppressUnmanagedCodeSecurityAttribute()]
internal static extern bool DeleteForm(IntPtr phPrinter, [MarshalAs(UnmanagedType.LPTStr)] string pName);
[DllImport("winspool.Drv", EntryPoint = "SetForm", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = false, CallingConvention = CallingConvention.StdCall), SuppressUnmanagedCodeSecurityAttribute()]
internal static extern bool SetForm(IntPtr phPrinter, [MarshalAs(UnmanagedType.LPTStr)] string pName, [MarshalAs(UnmanagedType.I4)] int level, ref structFormInfo1 form);
[DllImport("kernel32.dll", EntryPoint = "GetLastError", SetLastError = false, ExactSpelling = true, CallingConvention = CallingConvention.StdCall), SuppressUnmanagedCodeSecurityAttribute()]
internal static extern Int32 GetLastError();
[DllImport("GDI32.dll", EntryPoint = "CreateDC", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = false, CallingConvention = CallingConvention.StdCall), SuppressUnmanagedCodeSecurityAttribute()]
internal static extern IntPtr CreateDC([MarshalAs(UnmanagedType.LPTStr)]string pDrive, [MarshalAs(UnmanagedType.LPTStr)] string pName, [MarshalAs(UnmanagedType.LPTStr)] string pOutput, ref structDevMode pDevMode);
[DllImport("GDI32.dll", EntryPoint = "ResetDC", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = false, CallingConvention = CallingConvention.StdCall), SuppressUnmanagedCodeSecurityAttribute()]
internal static extern IntPtr ResetDC(IntPtr hDC, ref structDevMode pDevMode);
[DllImport("GDI32.dll", EntryPoint = "DeleteDC", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = false, CallingConvention = CallingConvention.StdCall), SuppressUnmanagedCodeSecurityAttribute()]
internal static extern bool DeleteDC(IntPtr hDC); public System.Drawing.Printing.PaperSize GetPrintForm(string printerName, string paperName)
{
System.Drawing.Printing.PaperSize paper = null;
System.Drawing.Printing.PrinterSettings printer = new System.Drawing.Printing.PrinterSettings();
printer.PrinterName = printerName;
foreach (System.Drawing.Printing.PaperSize ps in printer.PaperSizes)
{
if (ps.PaperName.ToLower() == paperName.ToLower())
{
paper = ps;
break;
}
}
return paper;
} public void SetPrintForm(string printerName, string paperName, float width, float height)
{
if (PlatformID.Win32NT == Environment.OSVersion.Platform)
{
const int PRINTER_ACCESS_USE = 0x00000008;
const int PRINTER_ACCESS_ADMINISTER = 0x00000004;
structPrinterDefaults defaults = new structPrinterDefaults();
defaults.pDatatype = null;
defaults.pDevMode = IntPtr.Zero;
defaults.DesiredAccess = PRINTER_ACCESS_ADMINISTER | PRINTER_ACCESS_USE;
IntPtr hPrinter = IntPtr.Zero;
if (OpenPrinter(printerName, out hPrinter, ref defaults))
{
try
{
structFormInfo1 formInfo = new structFormInfo1();
formInfo.Flags = 0;
formInfo.pName = paperName;
formInfo.Size.width = (int)(width * 100.0);
formInfo.Size.height = (int)(height * 100.0);
formInfo.ImageableArea.left = 0;
formInfo.ImageableArea.right = formInfo.Size.width;
formInfo.ImageableArea.top = 0;
formInfo.ImageableArea.bottom = formInfo.Size.height;
bool rslt = false;
if (GetPrintForm(printerName, paperName) != null)
{
rslt = SetForm(hPrinter, paperName, 1, ref formInfo);
}
else
{
this.AddCustomPaperSize(printerName, paperName, width, height);
rslt = true;
}
if (!rslt)
{
StringBuilder strBuilder = new StringBuilder();
strBuilder.AppendFormat("添加纸张{0}时发生错误!, 系统错误号: {1}", paperName, GetLastError());
MessageBox.Show(strBuilder.ToString());
}
}
finally
{
ClosePrinter(hPrinter);
}
}
}
} public void AddCustomPaperSize(string printerName, string paperName, float width, float height)
{
if (PlatformID.Win32NT == Environment.OSVersion.Platform)
{
const int PRINTER_ACCESS_USE = 0x00000008;
const int PRINTER_ACCESS_ADMINISTER = 0x00000004;
structPrinterDefaults defaults = new structPrinterDefaults();
defaults.pDatatype = null;
defaults.pDevMode = IntPtr.Zero;
defaults.DesiredAccess = PRINTER_ACCESS_ADMINISTER | PRINTER_ACCESS_USE;
IntPtr hPrinter = IntPtr.Zero;
if (OpenPrinter(printerName, out hPrinter, ref defaults))
{
try
{
DeleteForm(hPrinter, paperName);
structFormInfo1 formInfo = new structFormInfo1();
formInfo.Flags = 0;
formInfo.pName = paperName;
formInfo.Size.width = (int)(width * 100.0);
formInfo.Size.height = (int)(height * 100.0);
formInfo.ImageableArea.left = 0;
formInfo.ImageableArea.right = formInfo.Size.width;
formInfo.ImageableArea.top = 0;
formInfo.ImageableArea.bottom = formInfo.Size.height;
if (!AddForm(hPrinter, 1, ref formInfo))
{
StringBuilder strBuilder = new StringBuilder();
strBuilder.AppendFormat("添加纸张{0}时发生错误!, 系统错误号: {1}", paperName, GetLastError());
throw new ApplicationException(strBuilder.ToString());
}
}
finally
{
ClosePrinter(hPrinter);
}
}
else
{
StringBuilder strBuilder = new StringBuilder();
strBuilder.AppendFormat("打开打印机{0} 时出现异常!, 系统错误号: {1}", printerName, GetLastError());
throw new ApplicationException(strBuilder.ToString());
}
}
else
{
structDevMode pDevMode = new structDevMode();
IntPtr hDC = CreateDC(null, printerName, null, ref pDevMode);
if (hDC != IntPtr.Zero)
{
const long DM_PAPERSIZE = 0x00000002L;
const long DM_PAPERLENGTH = 0x00000004L;
const long DM_PAPERWIDTH = 0x00000008L;
pDevMode.dmFields = (int)(DM_PAPERSIZE | DM_PAPERWIDTH | DM_PAPERLENGTH);
pDevMode.dmPaperSize = 256;
pDevMode.dmPaperWidth = (short)(width * 2.54 * 10000.0);
pDevMode.dmPaperLength = (short)(height * 2.54 * 10000.0);
ResetDC(hDC, ref pDevMode);
DeleteDC(hDC);
}
}
} [DllImport("Winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool SetDefaultPrinter(string printerName); [DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool GetDefaultPrinter(StringBuilder pszBuffer, ref int pcchBuffer); public static bool SetDefaultPrint(string printName)
{
return SetDefaultPrinter(printName);
} /// <summary>
/// 获取所有打印机名称
/// </summary>
/// <returns></returns>
public List<string> GetLocalPrinters()
{
List<string> fPrinters = new List<string>();
string defaultprint = DBPaperSize.GetDefaultPrinter();
if (!string.IsNullOrEmpty(defaultprint))
{
fPrinters.Add(defaultprint);
}
foreach (string fPrinterName in PrinterSettings.InstalledPrinters)
{
if (!fPrinters.Contains(fPrinterName))
fPrinters.Add(fPrinterName);
}
//fPrinters = fPrinters.Where(item => item.ToLower().Contains("zdesigner")).ToList();
return fPrinters;
} /// <summary>
/// 获取默认的打印机
/// </summary>
/// <returns></returns>
public static string GetDefaultPrinter()
{
const int ERROR_FILE_NOT_FOUND = 2; const int ERROR_INSUFFICIENT_BUFFER = 122; int pcchBuffer = 0; if (GetDefaultPrinter(null, ref pcchBuffer))
{
return null;
} int lastWin32Error = Marshal.GetLastWin32Error(); if (lastWin32Error == ERROR_INSUFFICIENT_BUFFER)
{
StringBuilder pszBuffer = new StringBuilder(pcchBuffer);
if (GetDefaultPrinter(pszBuffer, ref pcchBuffer))
{
return pszBuffer.ToString();
} lastWin32Error = Marshal.GetLastWin32Error();
}
if (lastWin32Error == ERROR_FILE_NOT_FOUND)
{
return null;
}
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
(2) 网页获取本地连接打印机
其实这个比较简单,就是将客户端启动时候,获取本地连接的打印机,然后发送到服务端,然后服务端打开选择打印机时获取本地注册的打印机。注册的打印机信息使用Redis缓存,为什么使用Redis 是有用处的.
(3)将网页发送的执行传递给打印客户端
其实我们知道Redis除了缓存功能之外,还有简单的订阅发布的功能,将消息传递给打印客户端我们暂时就是使用的Redis,因为属于小量的消息发布订阅个人觉得Redis还比较合适,但是问题也会有,也有很多其他的方式解决。
(4) 打印机的上线和下线
这个其实就是取决于打印客户端是否启动了,其实是否真正连接到打印其实还是打印客户端说了算。当打印客户端启动的时候,将本地打印机信息发送到服务端,关闭的时候发送请求注销服务端缓存的打印机信息。所以为了能够完整的实现打印,打印客户端必须启动,这也是我们在实施过程中很难搞的部分,因为要打开浏览器又要打开客户端(目前我们的办法是设置开机自动启动打印客户端),客户觉得很麻烦。
另外Redis 掉线的问题,必须考虑重连,否则就无法接受到推送的信息,另外重复连接又导致消息订阅重复,导致一个指令可以打印多张标签出来。
五 将打印客户端注册成本地服务
上面说的这种方式是我们用的比较多的一种方式打印,但是有点就是如果有多个打印机站点,我选择的时候可以看到全工厂所有连接的打印机,这个选择就很头疼了。于是就出现了本机网页只调用本地的打印机服务。
这个时候我们就需要将本地的打印服务发布成一个可调用的http服务,IP地址指向127.0.0.1 ,由JS调用本地服务打印,至于如何转化为http服务,可以参考文章
<<吉特日化MES&WMS系统--三色灯控制协议转http>>
六 WebSocket 解决通讯问题
前面也提到了,解决问题的思路和方式都比较简单,也比较固话,只要解决操作端和打印机之间的通讯问题即可,我们还可以使用到WebSocket方式连接网页与打印客户端程序。
七 Windows 强大的注册表
你知道从淘宝网页上如何打开一个 阿里旺旺 聊天工具么,怎么在网页上唤醒 QQ 客服,这两个给了我一个比较不错的思路,我通过网页唤醒一个打印客户端程序,及时客户端未打开的情况下。
《吉特仓库管理系统(开源)-如何在网页端启动WinForm 程序》
只要能够启动打印客户端,就能够传递参数发送打印指令。
八 移动终端调用打印
说到这里绝大部分想到的可以使用蓝牙打印机,没错,蓝牙打印机的确是很不错的一个选择,但是有一点就是打印指令的编排问题,以及难以设置的打印格式。再就是我们开发移动终端的时候使用的是H5, 调用蓝牙通讯这个稳定性实在不敢恭维(当然也有可能是我们的技术能力太弱了,不求甚解)。
其实我们的方式还是采用 4 章节提到的思路,H5 ,http 协议这个不是很完美的方式么,H5 终端发送http指令,通过Redis推送打印消息到打印客户端
选择好打印机之后,然后确定就可以顺利打印出标签了。
以上的方式和方法在之前分享文字以及共享的代码中都有提到使用,只是这一次是集中汇总一次,虽然是小功能但是感觉是应用软件的刚需问题,说简单也简单说复杂也还有那么一些小的弯弯绕绕。
在这几年的工作中,虽然很多精力被分散到其他的事情上去了,但是也从其他领域学到了很多东西,希望有更多的时间能够记录这些经历和经验,这几年的工作领域跨度是非常大的,虽然年纪上来了也有些迷茫和担忧,但是一腔热血暂时还没有消退。世界之大,制造业领域的应用也跨越了众多学科,要学的东西还很多,虽然和高喊着 智能制造,数字化转型,工业互联网,5G+,大数据工业应用的专家和大佬比起来有很大的差距,自己更希望能够从工业制造的工艺应用上去有点突破,虽然要顺势而为但也要实事求是。
这几年主要工作都是从事 日化产品,化妆品类的工厂生产制造,包括 家用清洗剂,牙膏,香水,粉底彩妆,护肤,香水,保健品以及制药方面,希望有更多自己的时间来反思记录这些年遇到的问题。
吉特日化MES系统--通过浏览器调用标签打印的更多相关文章
- 吉特日化MES&WMS系统--三色灯控制协议转http
关于硬件控制大部分都是使用CS客户端程序,一般连接口都是用网口,串口,USB口等,应用通讯是不支持HTTp协议操作的,而目前一般做技术的人员都在于BS开发,使用HTTP 协议,所以在硬件交互上可能觉得 ...
- 织梦(dedecms)系统常用全局变量调用标签及路径
{dede:global.cfg_memberurl/} 指的是会员中心 对应/member/目录 {dede:global.cfg_cmsurl/} 对应的是网站根目录/ {dede:global. ...
- 吉特仓储管系统(开源WMS)--Web在线报表以及打印模板分享
很早之前就想写这篇文章与大家分享一下自己在吉特仓储管理系统中开发打印和报表的功能,在GitHub(https://github.com/hechenqingyuan/gitwms)上公开下载的代码中很 ...
- 吉特日化MES-生产制造的几种形态
1. 订货型和备货型 工厂的生产形态是以接受订单时间和开始生产时间来划分的,因为生产要么得到销售指令要么得到备货指令不能无缘无故的生产.销售指令驱动生产直接受市场销售影响,而备货型可能是对市场的一种预 ...
- 吉特日化MES-工业生产盲区
工业生产的几大盲区 1 重硬件忽略软件 : 目前只要提到智能化,大家都是想到的是一大堆自动执行的设备,什么机器人,输送线,人脸识别摄像头等,在一成套的系统中可能硬件几百万上千万,软件可以是几万几千几 ...
- 吉特仓储管系统(开源WMS)--分享两月如何做到10W+的项目
在此文开篇之处先特别申明,此文在有些人的眼中会有广告的嫌疑,但是本人不想将其作为一个广告宣传的文章,在此提到软件内容部分请大家予以谅解和包含,作为时间不算短的程序员给大家分享一些自己开发吉特仓储管理软 ...
- 吉特仓库管理系统-.NET打印问题总结
在仓储系统的是使用过程中避免不了的是打印单据,仓库系统中包含很多单据:入库单,出库单,盘点单,调拨单,签收单等等,而且还附带着很多的条码标签的打印.本文在此记录一下一个简单的打印问题处理方式.处理问题 ...
- Selenium+Python浏览器调用:Firefox
如何查看python selenium的API python -m pydoc -p 4567 说明: python -m pydoc表示打开pydoc模块,pydoc是查看python文档的首选工 ...
- 转:HTML5页面如何在手机端浏览器调用相机、相册功能
HTML5页面如何在手机端浏览器调用相机.相册功能 开发微信端浏览器访问的HTML5的页面,页面中有一个<input id="input" type="file&q ...
- MES系统在小批量电子行业生产管理中的应用
小批量电子产品生产管理的主要问题 电子电器制造类企业,既有单件小批量生产,也有批量生产:有按库存生产,也有按订单生产,属于典型的离散制造行业.因产品的不同其生产工艺流程也不尽相同,生产设备的布置不是按 ...
随机推荐
- Go 语言中排序的 3 种方法
原文链接: Go 语言中排序的 3 种方法 在写代码过程中,排序是经常会遇到的需求,本文会介绍三种常用的方法. 废话不多说,下面正文开始. 使用标准库 根据场景直接使用标准库中的方法,比如: sort ...
- 微信小程序上传文件操作示范
社会实践心得体会格式要求 提交的心得体会应为word文档,且图文并茂,全文段前.段后0,1.5倍行距. 题目:自拟,方正小标宋简体,小二号,加粗,居中. 个人信息:题目下方,宋体,小四号,加粗,居中, ...
- 了解 HarmonyOS
引言 在开始 HarmonyOS 开发之前,了解其背景.特点和架构是非常重要的.本章将为你提供一个全面的 HarmonyOS 概览. 目录 什么是 HarmonyOS HarmonyOS 的发展历程 ...
- Linux 主机磁盘繁忙度监控实战shell脚本
Linux 磁盘繁忙度是指磁盘的使用率和活动水平.可以通过一些工具来监测磁盘繁忙度,如 iostat.iotop.sar 等. 其中,iostat 是一个常用的工具,可以提供关于磁盘活动的详细统计信息 ...
- 一款国产开源 Web 防火墙神器!
随着开源 Web 框架和各种建站工具的兴起,搭建网站已经是一件成本非常低的事情,但是网站的安全性很少有人关注,以至于 WAF 这个品类也鲜为人知. 一.WAF 是什么? WAF 是 Web 应用防火墙 ...
- MySQL数据库触发器讲解 [创建/删除/查询/select into]
刚学习实例完mysql触发器, 前来分享学习经验. 菜鸟装逼, 老鸟勿喷 先来认识一下有关触发器的一些关键词. 在使用触发器时, 这些关键词将被用到, 请记下它们的模样和用途(意思) cre ...
- 在线问诊 Python、FastAPI、Neo4j — 创建 饮食节点
目录 饮食数据 创建节点 根据疾病.症状,判断出哪些饮食不能吃,哪些建议多吃 饮食数据 foods_data.csv 建议值用""引起来.避免中间有,号造成误识别 饮食 " ...
- Redis 命令工具
--- Redis 命令工具 --- redis-server Redis 服务器启动命令 redis-cli shutdown 停止服务 redis-benchmark:性能测试工具,用于检测 Re ...
- JAVA实现单链表修改和删除数据节点
JAVA实现单链表修改和删除数据节点 一.修改单链表中的一个节点 ①实现思路 因为带头节点的链表中头节点的next域不能发生改变(始终指向单链表的头节点),否则将找不到该链表.所以我们需要先找一个辅助 ...
- Springboot实现注解判断权限
Springboot实现注解判断权限 今天记录一下使用springboot的注解来给方法加权限 避免了每个方法都需要大量的权限判断 超级好用√ @ 目录 Springboot实现注解判断权限 1.创建 ...