欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

web打包和IIS配置

程序员文章站 2024-02-03 20:14:16
...

关于打包web项目很简单,但是要顺便配置IIS,做到安装完后就能用的话,要重写Installer类的Install方法.

网上找了很多套方案,结果都有问题,东拼西凑折腾好几天终于做出来了.

准备:

1,web发布文件(里面可以放其他东西,不影响,也可以是web发布的文件夹)

2,vs(我是2017,也不影响)

3,要能创建visual studio installer项目,没有这个要在拓展和更新搜索下载,如下图:

web打包和IIS配置

废话不多说,开始吧

一:新建web setup安装项目,可以和其他项目不在同目录下,或者干脆新建个解决方案:

web打包和IIS配置

 

二:右键解决方案>添加>现有网站>选择web发布文件夹

web打包和IIS配置

 

三:新建类库(名字随意)>在类库下新建安装程序类

web打包和IIS配置

web打包和IIS配置

web打包和IIS配置

注意:记得是安装程序类,不是普通的类,

打开类后有单击此处切换到代码视图这个东东,并且自动继承安装类

 [RunInstaller(true)]
 public partial class WebCardInstaller : Installer

配置IIS就在重写这个类的方法里:

public override void Install(IDictionary stateSaver)

 

四:右键安装项目>视图>用户界面>右键Start>添加对话框>选择你要的界面

常用的就是文本框AB,一个用来输入数据库配置,一个IIS配置,一个许可条款(不要也行),

web打包和IIS配置

注意:自己添加的框要拖拽到欢迎使用和安装地址之间,要不会有波浪线警告.

分别在文本框AB的属性上设置参数,由于我没在这装数据库,所以就贴IIS的参数吧

web打包和IIS配置

注意了:property这个属性很重要,代码区要根据这个读取value里的值和拼接关键字属性字符串,默认全是大写的.

如果只要3个参数,把其中一个visible设为false即可.

数据库类似的,传入必要的服务器名,账号,密码,数据库名即可.

 

五:右键安装项目>视图>文件系统>右键web安装文件夹>添加>项目输出>选择web发布文件夹和刚刚添加的类库

添加类库和发布文件选的方式也不同,还可以直接把发布文件夹整个拖进去,这样只要加类库输出,但是不建议这样,因为拖进去的文件不能操作,删除替换全都不行.

web打包和IIS配置

web打包和IIS配置

在文件系统的属性界面可以设置IIS的参数,比如默认起始页,读写权限,访问脚本权限等,也可以用代码来设置.

 

六:右键安装项目>视图>启动条件>在IIS Condition的属性上,把启动版本改下

web打包和IIS配置

 

七:右键安装项目>视图>自定义操作,在install(安装)再右键添加操作

添加在步骤五添加的主输出(类库)

web打包和IIS配置

注意:箭头指向的那个add output可以指定到web项目,据说这样就不用发布文件了,打包的时候会自动提取发布文件,但是我试过,直接把dll文件给打包了...

而且这样搞需要把安装项目和web项目放在同个解决方案.

添加后:

web打包和IIS配置

uninstall是卸载,也可以这样搞,但是感觉默认的卸载够用了,就不加了.

关键的来了,还记得在步骤四里设置的属性参数吗?在这需要用到它们来拼接关键字了,格式如下:

/iis=[IISSERVER] /ip=[IP] /port=[PORT] /isname=[ISNAME] /targetdir="[TARGETDIR]\"

中括号里大写的字符是步骤四里的property属性值,小写的是在代码中用来读取的关键字,可以看成是key.

每组关键字有个空格,最后有眼号的是IIS安装路径,眼号不能省略,最后一个是反斜杠 \

如果要装数据库,则数据库界面的属性也要加进来.

选中主输出,切换到属性,贴上上面拼接的关键字:

web打包和IIS配置

 

补充下,许可条款那个界面只要在文件系统传入一个文档,用户界面右键条款添加操作,指定文件系统里的文档就行,可有可无的东西就不弄了.

现在,所有能用鼠标点的设置都做完了,下面进入重点---代码.

思路是这样:

把服务器,端口,IP,站点ID等设置成字段,这样值设置一次,参数也不用传来传去弄的眼花.

必要的操作有:判断网站名和端口是否存在,站点ID取最小的,创建快捷方式,记录网站信息

主方法:

 

Context.Parameters就是获取用户输入的参数,
字段:
        private string iis;//服务器
        private string port;//端口
        private string serverID;//站点ID
        private string ip;//IP

        private DirectoryEntry _iisServer;
        private ManagementScope _scope;
        private ConnectionOptions _connection;

 

 

        /// <summary>
        /// 安装设置
        /// </summary>
        /// <param name="stateSaver"></param>
        public override void Install(IDictionary stateSaver)
        {
            base.Install(stateSaver);
            /*/iis=[IISSERVER] /ip=[IP] /port=[PORT] /isname=[ISNAME] /targetdir="[TARGETDIR]\"*/
            ip = Context.Parameters["ip"].ToString();
            iis = Context.Parameters["iis"].ToString();
            port = Context.Parameters["port"].ToString();
            string defaultVrootPath = Context.Parameters["targetdir"];
            serverID = GetNextOpenID().ToString();//获得一个最小ServerID
            string serverComment = Context.Parameters["isname"].ToString();//网站名
            if (IsExistSitePort())//判断端口重复
            {
                throw new Exception("端口号已被占用!");
            }
            Connect();// 连接IIS服务器

            if (defaultVrootPath.EndsWith(@"\"))//检查是否\结尾
            {
                defaultVrootPath = defaultVrootPath.Substring(0, defaultVrootPath.Length - 1);
            }

            string sReturn = CreateWebSite(serverComment, defaultVrootPath);

            string _path = defaultVrootPath + @"\App_Data\";
            SaveLog(_path, serverID, "serverID");//保存网站信息
            SaveLog(_path, port, "port");//保存网站信息
            SaveLog(_path, sReturn, "result");//保存网站信息

            CreateShortcut(port);//创建快捷方式
        }

 

获取最小网站ID是获取IIS虚拟目录下的所有站点ID,一个个比大小,最后返回一个最大的加1

 
/// <summary> /// 获得一个最小ServerID /// </summary> /// <returns></returns> private int GetNextOpenID() { //DirectoryEntry iisComputer = new DirectoryEntry("IIS://localhost/w3svc"); DirectoryEntry iisComputer = new DirectoryEntry("IIS://" + iis + "/w3svc"); int nextID = 0, name = 0; string sname = ""; foreach (DirectoryEntry iisWebServer in iisComputer.Children) { sname = iisWebServer.Name; try { name = int.Parse(sname); if (name > nextID) { nextID = name; } } catch { } } return ++nextID; }

 

 


根据用户输入的端口直接测试连接来判断是否已有

 

 

 

 

        /// <summary>
        /// 端口号是否重复
        /// </summary>
        /// <returns></returns>
        private bool IsExistSitePort()
        {
            Process p = new Process();
            p.StartInfo = new ProcessStartInfo("netstat", "-a");
            p.StartInfo.CreateNoWindow = true;
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
            p.StartInfo.RedirectStandardOutput = true;
            p.Start();
            string result = p.StandardOutput.ReadToEnd();
            if (result.IndexOf(Environment.MachineName.ToLower() + ":" + port) >= 0)
            {
                //端口被占用
                return true;
            }

            else
            {
                return false;
            }
        }


添加网站时,主要设置网站的属性,注意大小写

 

 

 

 

        /// <summary>
        /// 添加网站
        /// </summary>
        /// <param name="serverComment"></param>
        /// <param name="defaultVrootPath"></param>
        /// <param name="HostName"></param>
        /// <returns></returns>
        private string CreateWebSite(string serverComment, string defaultVrootPath)
        {
            try
            {
                ManagementObject oW3SVC = new ManagementObject(_scope, new ManagementPath(@"IIsWebService='W3SVC'"), null);

                if (IsWebSiteExists(serverID))//判断同名网站
                {
                    throw new Exception("已有同名网站");
                }
                ManagementBaseObject inputParameters = oW3SVC.GetMethodParameters("CreateNewSite");
                ManagementBaseObject[] serverBinding = new ManagementBaseObject[1];

                serverBinding[0] = CreateServerBinding();//绑定IP地址和端口
                inputParameters["ServerComment"] = serverComment;//网站名
                inputParameters["ServerBindings"] = serverBinding;//网站绑定的数据(IP和端口)
                inputParameters["PathOfRootVirtualDir"] = defaultVrootPath;//安装路径
                inputParameters["ServerId"] = serverID;//网站ID

                ManagementBaseObject outParameter = null;
                outParameter = oW3SVC.InvokeMethod("CreateNewSite", inputParameters, null);

                // 启动网站
                string serverName = "W3SVC/" + serverID;
                ManagementObject webSite = new ManagementObject(_scope, new ManagementPath(@"IIsWebServer='" + serverName + "'"), null);
                webSite.InvokeMethod("Start", null);

                // 网站属性设置
                DirectoryEntry virtualDirectory = (DirectoryEntry)webSite.InvokeMethod("Create", new object[] { "IIsWebVirtualDir", "Root" });
                virtualDirectory.CommitChanges();//保存
                virtualDirectory.Properties["AccessExecute"][0] = false;//可执行文件
                virtualDirectory.Properties["AccessScript"][0] = true;// 可访问脚本
                virtualDirectory.Properties["EnableDefaultDoc"][0] = true;
                virtualDirectory.Properties["DefaultDoc"][0] = "Login.aspx";//默认开打文件
                virtualDirectory.CommitChanges();//保存
                virtualDirectory.Properties["AccessRead"][0] = true;// 读取
                virtualDirectory.Properties["AccessWrite"][0] = true;// 写入
                virtualDirectory.Properties["ContentIndexed"][0] = true;// 资源索引
                virtualDirectory.Properties["AppIsolated"][0] = 2;// 值 0 表示应用程序在进程内运行,值 1 表示进程外,值 2 表示进程池
                virtualDirectory.CommitChanges();//保存

                virtualDirectory.Invoke("AppCreate", new object[] { 2 });
                virtualDirectory.CommitChanges();//保存

                return (string)outParameter.Properties["ReturnValue"].Value;
            }
            catch (Exception ex)
            {
                return ex.Message;
            }
        }

        /// <summary>
        /// 判断网站是否已经存在
        /// </summary>
        /// <param name="serverID"></param>
        /// <returns></returns>
        private bool IsWebSiteExists(string serverID)
        {
            try
            {
                string siteName = "W3SVC/" + serverID;
                ManagementObjectSearcher searcher = new ManagementObjectSearcher(_scope, new ObjectQuery("SELECT * FROM IIsWebServer"), null);
                ManagementObjectCollection webSites = searcher.Get();

                foreach (ManagementObject webSite in webSites)
                {
                    if ((string)webSite.Properties["Name"].Value == siteName)
                        return true;
                }
                return false;
            }
            catch
            {
                return false;
            }
        }


网站绑定注意了,

Hostname是站点名称,一般放空

 

 

        /// <summary>
        /// 网站绑定,绑定IP地址和端口
        /// </summary>
        /// <param name="HostName"></param>
        /// <returns></returns>
        private ManagementObject CreateServerBinding()
        {
            try
            {
                ManagementClass classBinding = new ManagementClass(_scope, new ManagementPath("ServerBinding"), null);
                ManagementObject serverBinding = classBinding.CreateInstance();
                serverBinding.Properties["Hostname"].Value = "";
                serverBinding.Properties["IP"].Value = ip;
                serverBinding.Properties["Port"].Value = port;
                serverBinding.Put();//提交更改
                return serverBinding;
            }
            catch
            {
                return null;
            }
        }


最后是创建快捷方式和保存网站信息:

 

 

 

 

        /// <summary>
        /// 创建快捷方式
        /// </summary>
        /// <param name="Port">端口</param>
        private void CreateShortcut(string Port)
        {
            //获取软件安装的物理目录
            //string urlpath = this.Context.Parameters["targetdir"];
            if (Port != "80")
            {
                string URL = "http://" + ip + ":" + Port + "/Login.aspx";
                string DeskPhath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
                //创建URL快捷方式(文件)
                DeskPhath = DeskPhath + "\\" + "你的名字.url";
                StreamWriter sw = new StreamWriter(File.Open(DeskPhath, FileMode.Create, FileAccess.ReadWrite));
                sw.WriteLine("[DEFAULT]");
                sw.WriteLine("BASEURL=" + URL);
                sw.WriteLine("[InternetShortcut]");
                sw.WriteLine("URL=" + URL);
                //sw.WriteLine("IconFile=" + SetupPath + "\\park.ico");
                // sw.WriteLine("IconIndex=0");
                sw.Flush();
                sw.Close();
            }
        }

        /// <summary>
        /// 保存网站信息
        /// </summary>
        /// <param name="path"></param>
        private void SaveLog(string path, string data, string name)
        {
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }
            path = path + name + ".txt";

            using (FileStream fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite))
            {
                StreamWriter sw = new StreamWriter(fs);
                sw.WriteLine(data);
                sw.Flush();
                sw.Close();
            }
        }

问我为什么不用xml?因为TXT文件代码更少web打包和IIS配置
 

 

最后右键安装项目,重新生成,然后在debug下会生成2安装文件,双击就能装了.

 

补充下:

因为项目需要,所以把安装数据库的功能搬出来了,就说下我的思路吧.

用winform来安装,

根据用户输入的数据生成SQL创建数据库语句,

把语句连接到数据库执行下就行,

如果创建数据库同时还要创建表和存储过程什么的,建议把数据库内容做成.sql格式的脚本,

先建库,接着use 数据库 go,再执行脚本,

注意:脚本里不能有建库语句,因为数据库执行是一次性加载,一次性执行的,而且GO必须单独一行,所以构建语句要注意.

安装数据库程序建议做成在网站根目录下执行,脚本也放那,这样可以同时读取脚本和web.config文件(读取当前目录文件).

创建好数据库后还要改web.config文件,用xml来操作,这样网站就和数据库对接了.

补个坑,创建安装项目的时候默认启动条件会选本机的最高版本,在没有安装相应版本的电脑安装会报错,我在这坑里趟了半天,差点怀疑人生,跟改IIS一样

右键安装项目,视图,启动条件,改.NET版本

web打包和IIS配置

end.