一、前言:

      最近做一个简单的在线升级Demo,使用了微软较早的.Net Remoting技术来练手。

简单的思路就是在服务器配置一个Remoting对象,然后在客户端来执行Remoting对象中的方法。

过程:

(1) 读取本地dll文件的名称与版本号,与服务器的进行对比

(2) 确认需要升级的文件名称与版本号并告诉服务器,服务器将其复制到一个临时文件夹并压缩成zip

(3) 将服务器的zip下载到本地的临时文件夹,并解压。

定义服务器端为UpdateServer,其配置文件为:

<configuration>
<system.runtime.remoting>
<application>
<service>
<wellknown type="UpdateLibrary.RemoteObject, UpdateLibrary" mode="Singleton" objectUri="RemoteObject.rem"/>
</service>
<channels>
<channel ref="http" port="">
</channel>
</channels>
</application>
</system.runtime.remoting>
<appSettings>
<!--<add key="Dir" value="E:\server"/>-->
</appSettings>
</configuration>

定义客户端为UpdateClient,其配置文件为:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="ServerUrl" value="127.0.0.1:8989"/>
<add key="Modules" value="BIMCoreDB"/>
<add key="BufferLength" value=""/>
</appSettings>
</configuration>

定义两端共同调用的dll为UpdateLibrary。

二、服务器端代码:

     程序主入口:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Configuration;
using UpdateLibrary; namespace UpdateServer
{
static class Program
{
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
LoadConfig();
Application.Run(new FormServer());
} private static void LoadConfig()
{
Config.Dir = System.IO.Path.Combine(Application.StartupPath, "serverfiles"); //更新包所在位置
Config.TempDir = System.IO.Path.Combine(Application.StartupPath, "temp"); //临时文件夹,用于放更新文件的地方。
}
}
}

服务器窗体后台代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.Remoting;
namespace UpdateServer
{
public partial class FormServer : Form
{
public FormServer()
{
InitializeComponent();
try
{
//remoting配置
RemotingConfiguration.Configure(string.Format("{0}\\UpdateServer.exe.config", Application.StartupPath), false);
}
catch (Exception e)
{
MessageBox.Show(this, e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void FormServer_Load(object sender, EventArgs e)
{
lbl_time.Text = "当前时间:"+DateTime.Now.ToString("T");
tm_Server = new Timer();
tm_Server.Tick += tm_Server_Tick;
tm_Server.Interval = ;
tm_Server.Enabled = true;
}
void tm_Server_Tick(object sender,EventArgs e)
{
lbl_time.Text = string.Empty;
lbl_time.Text = "当前时间:" + DateTime.Now.ToString("T");
}
}
}

三、UpdateLibrary:

UpdateLibrary类库包含三个类:
        (1)Config类:用于提取配置文件中的信息。

(2)ZipHelper类:第三方库,用于文件压缩与解压缩。

(3)RemoteObject类:remoting对象,实现两端之间所需要的方法。

Config代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq; namespace UpdateLibrary
{
/// <summary>
/// 将配置文件中的信息传给Config对象
/// </summary>
public static class Config
{
public static string Dir { get; set; }
public static string TempDir { get; set; }
public static string[] Modules { get; set; }
public static int BufferLength { get; set; }
public static string ServerUrl { get; set; }
}
}

        ZipHelper代码:(比较实用的压缩与解压缩方法)

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ICSharpCode.SharpZipLib.Zip;
using ICSharpCode.SharpZipLib.Checksums;
using System.IO; namespace UpdateLibrary
{
public class ZipHelper
{
#region 压缩
/// <summary>
/// 压缩文件
/// </summary>
/// <param name="sourceFilePath"></param>
/// <param name="destinationZipFilePath"></param>
public static void CreateZip(string sourceFilePath, string destinationZipFilePath)
{
if (sourceFilePath[sourceFilePath.Length - ] != System.IO.Path.DirectorySeparatorChar)
sourceFilePath += System.IO.Path.DirectorySeparatorChar;
ZipOutputStream zipStream = new ZipOutputStream(File.Create(destinationZipFilePath));
zipStream.SetLevel(); // 压缩级别 0-9
CreateZipFiles(sourceFilePath, zipStream);
zipStream.Finish();
zipStream.Close();
}
/// <summary>
/// 递归压缩文件
/// </summary>
/// <param name="sourceFilePath">待压缩的文件或文件夹路径</param>
/// <param name="zipStream">打包结果的zip文件路径(类似 D:\WorkSpace\a.zip),全路径包括文件名和.zip扩展名</param>
/// <param name="staticFile"></param>
private static void CreateZipFiles(string sourceFilePath, ZipOutputStream zipStream)
{
Crc32 crc = new Crc32();
string[] filesArray = Directory.GetFileSystemEntries(sourceFilePath);
foreach (string file in filesArray)
{
if (Directory.Exists(file)) //如果当前是文件夹,递归
{
CreateZipFiles(file, zipStream);
}
else //如果是文件,开始压缩
{
FileStream fileStream = File.OpenRead(file);
byte[] buffer = new byte[fileStream.Length];
fileStream.Read(buffer, , buffer.Length);
string tempFile = file.Substring(sourceFilePath.LastIndexOf("\\") + );
ZipEntry entry = new ZipEntry(tempFile);
entry.DateTime = DateTime.Now;
entry.Size = fileStream.Length;
fileStream.Close();
crc.Reset();
crc.Update(buffer);
entry.Crc = crc.Value;
zipStream.PutNextEntry(entry);
zipStream.Write(buffer, , buffer.Length);
}
}
}
#endregion #region 解压缩 public static void UnZip(Stream stream, string targetPath)
{
using (ZipInputStream zipInStream = new ZipInputStream(stream))
{
ZipEntry entry;
while ((entry = zipInStream.GetNextEntry()) != null)
{
string directorName = Path.Combine(targetPath, Path.GetDirectoryName(entry.Name));
string fileName = Path.Combine(directorName, Path.GetFileName(entry.Name));
// 创建目录
if (directorName.Length > )
{
Directory.CreateDirectory(directorName);
}
if (fileName != string.Empty && !entry.IsDirectory)
{
var ext = System.IO.Path.GetExtension(fileName);
using (FileStream streamWriter = File.Create(fileName))
{
int size = ;
byte[] data = new byte[ * ];
while (true)
{
size = zipInStream.Read(data, , data.Length);
if (size > )
{
streamWriter.Write(data, , size);
}
else break;
}
}
}
}
}
}
#endregion
}
}

        RemoteObject代码:

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
using System.Text;
using System.IO;
using System.Collections;
using Newtonsoft.Json; namespace UpdateLibrary
{
public class RemoteObject : MarshalByRefObject
{
public string GetUpdateFileVersion()
{
System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo(Config.Dir);
System.IO.FileInfo[] files = dir.GetFiles("*.dll"); //获取服务器端所有dll文件 List<string> fileinfo = new List<string>();//记录文件名与文件版本
foreach(var file in files)
{
string filename = System.IO.Path.GetFileNameWithoutExtension(file.ToString());//获取文件名称
fileinfo.Add(filename);
FileVersionInfo ver = FileVersionInfo.GetVersionInfo(System.IO.Path.Combine(Config.Dir, file.ToString()));
string fileversion = ver.FileVersion; //获取文件的版本
fileinfo.Add(fileversion);
}
string SendData = JsonConvert.SerializeObject(fileinfo);//转Json
return SendData;
} public IList CreateZipfile(string str_r)
{
List<string> templist = JsonConvert.DeserializeObject<List<string>>(str_r);//接收到确认更新的文件名 foreach (string filename in templist) //把需要更新的文件都复制到临时文件夹中
{
string updatefile = Path.Combine(Config.Dir, filename + ".dll");
File.Copy(updatefile, Path.Combine(Config.TempDir, filename + ".dll"), true);
System.IO.File.SetAttributes(Path.Combine(Config.TempDir, filename + ".dll"), System.IO.FileAttributes.Normal);//去掉文件只读属性
} string tempzippath=Path.Combine(Config.Dir,"tempzip");//临时压缩包路径,默认更新文件夹下的tempzip文件夹
if(Directory.Exists(tempzippath)==false) //判断是否有安放压缩包的地方
{
Directory.CreateDirectory(tempzippath);
} ZipHelper.CreateZip(Config.TempDir, Path.Combine(tempzippath, "Update.zip"));//将临时文件夹内的文件都压缩到tempzip文件夹下的update.zip
System.IO.FileInfo f = new FileInfo(Path.Combine(tempzippath,"Update.zip"));//获得该压缩包的大小
IList SendData = new ArrayList();
SendData.Add(Path.Combine(tempzippath, "Update.zip")); //得到压缩包名称
SendData.Add(f.Length); //得到压缩包文件大小
return SendData;
}
public byte[] GetFile(string name, int start, int length)
{
using (System.IO.FileStream fs = new System.IO.FileStream(System.IO.Path.Combine(Config.TempDir, name), System.IO.FileMode.Open, System.IO.FileAccess.Read, FileShare.ReadWrite))
{
byte[] buffer = new byte[length];
fs.Position = start;
fs.Read(buffer, , length);
return buffer;
}
} public void Finish(string name)
{
// File.Delete(System.IO.Path.Combine(Config.TempDir, name)); //删除压缩包文件夹
}
}
}

四、客户端端代码:

程序主入口:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Configuration;
using UpdateLibrary; namespace UpdateClient
{
static class Program
{
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
LoadConfig(args);
Application.Run(new FormClient());
}
private static void LoadConfig(string[] args)
{
Config.Dir = System.IO.Path.Combine(Application.StartupPath,"localfiles");//本地文件位置
Config.TempDir = System.IO.Path.Combine(Application.StartupPath, "temp");//本地放更新文件的位置
Config.ServerUrl = ConfigurationManager.AppSettings["ServerUrl"].ToString();//设置服务器Url
Config.Modules =ConfigurationManager.AppSettings["Modules"].ToString().Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);//更新文件的名称
Config.BufferLength = int.Parse(ConfigurationManager.AppSettings["BufferLength"].ToString()); //缓存大小
}
}
}

第一个窗体FormClient,用于比对文件,如果有更新则提供按钮进入更新窗体FormUpdate

 using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using UpdateLibrary;
using System.IO;
using Newtonsoft.Json; namespace UpdateClient
{
public partial class FormClient : Form
{
Dictionary<string, string> Localupdate = new Dictionary<string, string>(); //确认本地需要文件
Dictionary<string, string> ConfirmUpdate = new Dictionary<string, string>(); //确认需要更新的文件并告诉服务器的
Dictionary<string, string> ConfirmAdd = new Dictionary<string, string>(); //确认需要新增的文件
public FormClient()
{
InitializeComponent();
btn_update.Enabled = false;
int ScreenWidth = Screen.PrimaryScreen.WorkingArea.Width;
int ScreenHeight = Screen.PrimaryScreen.WorkingArea.Height;
int x = ScreenWidth - this.Width - ;
int y = ScreenHeight - this.Height - ;
this.Location = new Point(x, y);
} private void btn_update_Click(object sender, EventArgs e)
{
Form updatewindow = new FormUpdate(ConfirmUpdate);
updatewindow.Show();
this.Hide();
} private void FormClient_Load(object sender, EventArgs e)
{
Dictionary<string, string> localfileversion = new Dictionary<string, string>();
foreach (string module in Config.Modules)
{
string filepath = System.IO.Path.Combine(Config.Dir, module + ".dll");
FileVersionInfo ver = FileVersionInfo.GetVersionInfo(filepath);
string dllVersion = ver.FileVersion;
localfileversion.Add(module, dllVersion); //文件名-版本
} //文件对比
try
{
RemoteObject remoteObject = (RemoteObject)Activator.GetObject(typeof(RemoteObject), string.Format("http://{0}/RemoteObject.rem", Config.ServerUrl));
//获取服务器更新包的版本号 string SFVersion = remoteObject.GetUpdateFileVersion();//获取服务器端更新文件的名称与版本号
List<string> Recieve = new List<string>();
Recieve = JsonConvert.DeserializeObject<List<string>>(SFVersion);//转成泛型
Dictionary<string, string> serverfileversion = new Dictionary<string, string>();//转成字典型
for (int i = ; i < Recieve.Count; i += )
{
serverfileversion.Add(Recieve[i], Recieve[i + ]);
} if (serverfileversion.Count > ) //是否有更新文件
{
foreach (var serverkey in serverfileversion.Keys)
{
if (localfileversion.ContainsKey(serverkey)) //本地是否有更新文件的名称,没有说明是新增的
{
if (localfileversion[serverkey] == serverfileversion[serverkey]) //版本号相同?
{
serverfileversion.Remove(serverkey);//不需要更新
}
else
{
ConfirmUpdate.Add(serverkey, serverfileversion[serverkey]); //确认更新的
Localupdate.Add(serverkey, localfileversion[serverkey]); //本地的版本
}
}
else
{
ConfirmAdd.Add(serverkey, serverfileversion[serverkey]);//确认新增文件,用于提示
}
}
}
else
{
lblmessage.Text = "暂无更新文件";
btn_update.Visible = false;
}
}
catch(Exception ex)
{
lblmessage.Text = ex.ToString();
btn_update.Visible = false;
} if(ConfirmAdd.Count== && ConfirmUpdate.Count==)
{
lblmessage.Text = "没有需要更新的模块";
btn_update.Visible = false;
}
else
{
string upinfo = string.Empty;
lblmessage.Text = "检测完成,需要更新";
btn_update.Enabled = true;
//显示更新的
if (ConfirmUpdate.Count>)
{
upinfo = "更新文件信息:\r\n\r\n";
foreach(var key in ConfirmUpdate.Keys)
{
upinfo += "文件名为:" + key + "\r\n" + "旧版本号:" + Localupdate[key] + "\r\n" + "新版本号:" + ConfirmUpdate[key] + "\r\n";
}
} //显示新增的
if (ConfirmAdd.Count > )
{
upinfo += "\r\n";
upinfo += "新增文件\r\n";
foreach (var key in ConfirmAdd.Keys)
{
upinfo += "文件名为:" + key + "\r\n" + "版本号为:" + ConfirmAdd[key] + "\r\n";
ConfirmUpdate.Add(key, ConfirmAdd[key]);
}
}
txt_UpdateMessage.Text = upinfo;
} }
private void FormClient_FormClosed(object sender, FormClosedEventArgs e)
{
Environment.Exit();
} }
}

第二个窗体FormUpdate,用于更新文件,这里同样使用了backgroundWorker控件来进行异步操作。

 using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using UpdateLibrary;
using Newtonsoft.Json;
using System.Collections;
using System.IO; namespace UpdateClient
{
public partial class FormUpdate : Form
{
Dictionary<string, string> serverupdatefiles = new Dictionary<string, string>();
public FormUpdate(Dictionary<string, string> confirmupdate)
{
InitializeComponent();
int ScreenWidth = Screen.PrimaryScreen.WorkingArea.Width;
int ScreenHeight = Screen.PrimaryScreen.WorkingArea.Height;
int x = ScreenWidth - this.Width - ;
int y = ScreenHeight - this.Height - ;
this.Location = new Point(x, y);
serverupdatefiles = confirmupdate; //获得需要更新的列表
} private void FormUpdate_Load(object sender, EventArgs e)
{
bgk_Update.RunWorkerAsync() ;
} private void FormUpdate_FormClosed(object sender, FormClosedEventArgs e)
{
Environment.Exit(); //终止该进程
} private void bgk_Update_DoWork(object sender, DoWorkEventArgs e)
{
try
{
RemoteObject remoteObject = (RemoteObject)Activator.GetObject(typeof(RemoteObject), string.Format("http://{0}/RemoteObject.rem", Config.ServerUrl));
bgk_Update.ReportProgress(, "准备更新..."); List<string> list_temp = new List<string>();
list_temp = DictToList(serverupdatefiles);//将确认更新的文件名转成泛型
string str_confirmupdate = JsonConvert.SerializeObject(list_temp);//转成Json //将确认的文件列表返回给server,使其压缩并放置在temp文件夹下
IList RecieveData = new ArrayList();
RecieveData = remoteObject.CreateZipfile(str_confirmupdate); string fileName = RecieveData[].ToString(); //获得压缩包的名称
int fileLength = Convert.ToInt32(RecieveData[]);//获得压缩包的大小 string filePath = Path.Combine(Config.TempDir,"Update.zip");//解压到本地临时文件夹下的Update.zip using (System.IO.FileStream stream = new System.IO.FileStream(filePath, System.IO.FileMode.Create))
{
for (int i = ; i < fileLength; i += Config.BufferLength * )
{
var percent = (int)((double)i / (double)fileLength * );
bgk_Update.ReportProgress(percent, "正在下载更新包...");
int length = (int)Math.Min(Config.BufferLength * , fileLength - i);
var bytes = remoteObject.GetFile(fileName, i, length);
stream.Write(bytes, , length);
}
stream.Flush();
}
remoteObject.Finish(fileName);
bgk_Update.ReportProgress(, "正在解压");
using (System.IO.FileStream stream = new System.IO.FileStream(filePath, System.IO.FileMode.Open, System.IO.FileAccess.Read))
{
ZipHelper.UnZip(stream, Config.TempDir);//解压获得更新文件
}
System.IO.File.Delete(filePath);//删除解压包
e.Result = "更新完成";
}
catch (Exception ex)
{
e.Result = ex;
}
} private void bgk_Update_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
lbl_message.Text = (string)e.UserState;
pbr_Update.Value = e.ProgressPercentage;
} private void bgk_Update_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Result is Exception)
{
lbl_message.Text = (e.Result as Exception).Message;
}
else
{
lbl_message.Text = (string)e.Result;
}
} public List<string> DictToList(Dictionary<string,string> dict)
{
List<string> templist = new List<string>();
foreach(var key in dict.Keys)
{
templist.Add(key);
}
return templist;
}
}
}

五、小结:

(1) 运用了Remoting技术,简单来说就是服务器通过config文件配置remoting服务;客户端调用这个remoting服务;

(2) 使用了BackgroundWorker控件,其主要是三种方法Dowork();ProgressChanged();RunWorkerCompleted()

其中控件的ReportProgress()方法可以通过ProgressChanged中的label与ProgressBar来显示升级状况与进度。

(3) 上篇以remoting方式叙述如何从服务器端下载文件到本地,下篇将介绍得到更新的dll后如何将调用旧dll的exe程序关闭再重启加载新dll的方法,从而实现更新。

【Remoting】.Net remoting方法实现简单的在线升级(上篇:更新文件)的更多相关文章

  1. 【Remoting】.Net remoting方法实现简单的在线升级(下篇:重启exe)

    一.前言      上篇运用了.Net Remoting技术解决了本地与服务器版本对比,并下载更新包的过程. 本篇主要是应用Process,来实现重启程序的过程. 情景假设:       Revit2 ...

  2. 【uniapp 开发】uni-app 资源在线升级/热更新

    注:本文为前端代码资源热更新.如果是整包升级,另见文档 https://ask.dcloud.net.cn/article/34972 HBuilderX 1.6.5 起,uni-app 支持生成 A ...

  3. 【转】Microsoft .Net Remoting之Remoting事件处理全接触

    Remoting事件处理全接触 前言:在Remoting中处理事件其实并不复杂,但其中有些技巧需要你去挖掘出来.正是这些技巧,仿佛森严的壁垒,让许多人望而生畏,或者是不知所谓,最后放弃了事件在Remo ...

  4. 基于PHP实现一个简单的在线聊天功能(轮询ajax )

    基于PHP实现一个简单的在线聊天功能(轮询ajax ) 一.总结 1.用的轮询ajax 二.基于PHP实现一个简单的在线聊天功能 一直很想试着做一做这个有意思的功能,感觉复杂的不是数据交互和表结构,麻 ...

  5. ListView与.FindControl()方法的简单练习 #2 -- ItemUpdting事件中抓取「修改后」的值

    原文出處  http://www.dotblogs.com.tw/mis2000lab/archive/2013/06/24/listview_itemupdating_findcontrol_201 ...

  6. PHP基础示例:简单的在线文件管理

    先截个图: 下面为代码部分,由于只有一个文件,所以就不折叠了. <?php //简单的在线文件管理 $path = "./"; $filelist=array("f ...

  7. 基于Server-Sent Event的简单聊天室 Web 2.0时代,即时通信已经成为必不可少的网站功能,那实现Web即时通信的机制有哪些呢?在这门项目课中我们将一一介绍。最后我们将会实现一个基于Server-Sent Event和Flask简单的在线聊天室。

    基于Server-Sent Event的简单聊天室 Web 2.0时代,即时通信已经成为必不可少的网站功能,那实现Web即时通信的机制有哪些呢?在这门项目课中我们将一一介绍.最后我们将会实现一个基于S ...

  8. js实用方法记录-简单cookie操作

    js实用方法记录-简单cookie操作 设置cookie:setCookie(名称,值,保存时间,保存域); 获取cookie:setCookie(名称); 移除cookie:setCookie(名称 ...

  9. 关于js的对象创建方法(简单工厂模式,构造函数模式,原型模式,混合模式,动态模式)

    // 1.工厂方式创建对象:面向对象中的封装函数(内置对象) 简单来说就是封装后的代码,简单的工厂模式是很好理解的,关于它的作用,就是利用面向对象的方法,把一些对象封装,使一些占用空间多的,重复的代码 ...

随机推荐

  1. Wix 安装部署教程(十四) -- 多语言安装包之用户许可协议

    在上一篇中,留下了许可协议的问题,目前已经解决.感谢网友武全的指点! 问题 一般我们是用WixVariable 来设定许可协议.如下所示: <WixVariable Id="WixUI ...

  2. 用1、2、2、3、4、5这六个数字,用java写一个main函数,打印出所有不同的排列,如:512234、412345等,要求:"4"不能在第三位,"3"与"5"不能相连。

    最近在看算法,看到这个题,觉得挺经典的,收起. 分析: 1 .把问题归结为图结构的遍历问题.实际上6个数字就是六个结点,把六个结点连接成无向连通图,对于每一个结点求这个图形的遍历路径,所有结点的遍历路 ...

  3. 细说.NET中的多线程 (六 使用MemoryBarrier,Volatile进行同步)

    上一节介绍了使用信号量进行同步,本节主要介绍一些非阻塞同步的方法.本节主要介绍MemoryBarrier,volatile,Interlocked. MemoryBarriers 本文简单的介绍一下这 ...

  4. web系统架构设计中需要知道的点(前端篇)

    上周没写东西,这周写点互联网系统开发中需要了解的技术点,每个点都可以发散出去,连接更多的知识点,打算做个逐步细化的记录. 一个应用的整个生命周期中(生,老,病,死)都需要有一个整体规划. 前期 评估需 ...

  5. MVVM架构~knockoutjs实现简单的购物车

    返回目录 概念相关 购物车相信大家都用过,很方便,可以将多个商品添加到购物车,并且可以修改购买商品的数据,当然为了用户体验好,在修改数据时,你的价格也会出现变化的,这使用JS可以实现,但我认为,代码量 ...

  6. Atitit  java jsp 新的tag技术

    Atitit  java jsp 新的tag技术 1.1.  Tag Files  vs 原生写 SimpleTag 比较麻烦的 JSP 1.x 允许 Web 开发人员创建 Java 组件(称为标记处 ...

  7. 大数据平台R语言web UI应用架构 设计与开发

    1. 系统拓扑图 在日常业务分析中,R是非常常用的分析工具,而当数据量较大时,用R语言需要需用更多的时间来完成训练模型,spark作为大规模数据处理框架,采用内存计算,可以短时间内完成大量的数据的处理 ...

  8. PHP实现RESTful风格的API实例(一)

    最近看了一些关于RESTful的资料,自己动手也写了一个RESTful实例,以下是源码 目录详情: restful/ Request.php 数据操作类 Response.php 输出类 index. ...

  9. java常用命令行指令

    javac 将java源文件编译成class字节码文件 javac HelloWorld.java java (1)运行class文件 java HelloWorld 注意java命令后面不要加.cl ...

  10. C++学习之重载、覆盖与隐藏

    MaiziTest12.cpp : 定义控制台应用程序的入口点. 1.重载特征 1)相同的范围(在同一个类中): 2)函数名相同: 3)参数不同: 4)virtual关键字可有可无. 2.覆盖 指的是 ...