如果App_Data目录不存在,我该如何让msdeploy创建它,但不删除远程目录中的任何内容?

29
我已经设置了以下的Package/Publish Web设置来安装应用程序:
  • 仅包含运行此应用程序所需的文件
  • (未选中) 排除生成的调试符号
  • (选中) 排除来自App_Data文件夹的文件
  • (选中) 包括在 Package/Publish SQL 选项卡中配置的所有数据库 - 注意我没有配置任何数据库
  • (未选中) 将IIS设置包含为在IIS Express中配置的那样
在项目中,我设置了一个App_Data文件夹,主要用于处理应用程序日志。
我希望看到(并期望)的行为如下:
  1. 在首次部署到全新服务器时,将复制应用程序并创建一个具有分配给应用程序的写权限的App_Data文件夹。
  2. 在后续部署中,将忽略App_Data文件夹,因为它已经存在,并且“排除来自App_Data文件夹的文件”被选中。
然而,msdeploy似乎没有执行步骤#1(如果我手动创建文件夹,则步骤2是可以的)。除了这个未回答的问题之外,我无法在网络上找到任何文档来证实我看到的行为。
在这种情况下,如何让msdeploy创建App_Data并在初始部署时分配权限?
4个回答

19

从头开始部署时如何获取App_Data

@tdykstra已经正确处理了。为了将App_Data部署到服务器上(并自动设置ACLs),我执行了以下操作:

  1. 在App_Data中添加一个占位符文件。
  2. 将占位符的生成操作设置为内容(我的占位符文件中有文字,让那些偶然发现它的人知道它存在的原因)。
  3. 取消选中“在VS 2010项目属性的Package/Publish Web选项卡中排除App_Data文件夹中的文件”。

这样就可以创建并准备好我的App_Data文件夹以在服务器上使用。但是,每次重新发布时都会导致所有文件被删除。这是我上面提到的问题#2,并且与此其他SO question/answer非常相似。

防止服务器上的数据在后续发布事件中被删除

MsDeploy有两种机制可能会混淆(至少我混淆了):

  1. 排除文件
  2. MsDeploy跳过规则

这两者都可以用来解决问题,具体取决于情况:

  1. @tdykstra的解决方案可能适用于您:
    1. 预先知道App_Data中文件的名称(例如sqllite数据库)
    2. 在项目中包含了App_Data文件夹中的文件
  2. 使用MsDeploy跳过规则告诉MsDeploy完全跳过该目录和该目录中的文件上的所有删除操作。 这解决了所有情况下的问题,但涉及更多步骤。

实施MsDeploy跳过规则

要实施跳过规则,您将不得不放弃VS 2010中的右键“部署”选项,改为右键“打包”,进入命令行,重新调整批处理文件并运行命令行)。 如果您愿意忍受这种体验(我愿意,因为我通过CI流程自动化了所有内容),以下是详细信息:

  1. Edit the project file and add the following. Note that the AbsolutePath argument is a regular expression, so you can get way fancy:

    <Target Name="AddCustomSkipRules">
        <ItemGroup>
          <MsDeploySkipRules Include="SkipDeleteAppData">
            <SkipAction>Delete</SkipAction>
            <ObjectName>filePath</ObjectName>
            <AbsolutePath>$(_Escaped_PackageTempDir)\\App_Data\\.*</AbsolutePath>
            <XPath>
            </XPath>
          </MsDeploySkipRules>
          <MsDeploySkipRules Include="SkipDeleteAppData">
            <SkipAction>Delete</SkipAction>
            <ObjectName>dirPath</ObjectName>
            <AbsolutePath>$(_Escaped_PackageTempDir)\\App_Data\\.*</AbsolutePath>
            <XPath>
            </XPath>
          </MsDeploySkipRules>
        </ItemGroup>
    </Target>
    
  2. Package, do not deploy the project. This will create a zip file and .cmd file in the target directory (defined by "Location where package will be created" on the Package/Publish Web Tab). By default, this is obj\Debug\Package (or obj\Release\Package)
  3. Deploy the site using the the resulting command file
在我的测试中,您必须打包并运行命令文件。项目文件中的调整将告诉msbuild将必要的-skip规则放入命令文件中。但是,直接使用VS 2010中的“发布”功能似乎不会运行命令文件(请参见此演练上的警告)……它直接调用msdeploy,并且似乎不尊重项目文件的跳过规则。我认为这就是VS使用msbuild -T:Package和msbuild -T:MsDeployPublish构建项目之间的区别,但我还没有测试过。
最后,命令文件并不完全正确,至少在VS 2010 SP1中如此。这里有一个SO答案很好地描述了出了什么问题,但基本上,VS(或者可能/t:Package目标更为罪魁祸首)设置了发布到机器的命令文件而没有指定站点。为了解决这个问题,您需要以某种方式将"? site=sitename"(可能是? site=Default+Web+Site,对于https://machine:8172/MsDeploy.axd?site=Default+Web+Site的完整URL)附加到computerName参数的末尾。

我的问题是命令文件(批处理文件)在使用命令行时使用站点=任何内容都会出现问题,因为它错误地解析了命令行参数(即使转义)。我看不到除修改cmd文件之外的解决方法,但为了测试,我复制了我失败的测试运行中看到的msdeploy.exe输出,并修改了它来直接调用msdeploy.exe而不使用脚本。

现在它正在工作,我的意图是将其纳入我的CI构建过程中。我将为最终解决方案做以下事情:

  1. 将我的构建脚本更改为使用/T:Package(现在是/T:MsDeploy)
  2. 编写一个脚本化的搜索/替换例程来修改生成的cmd部署脚本
  3. 运行修改后的部署脚本

这应该会更容易一些。

更新

这是我在PowerShell中想出的脚本化搜索/替换例程:

(Get-Content "project.deploy.cmd") 
  -replace('^set _ArgComputerName=$'
           ,"set  ArgComputerName=https://server:8172/MsDeploy.axd?Site=Default+Web+Site") 
  | Out-File -Encoding ascii deploy.cmd

一旦运行了这个命令,就可以调用deploy.cmd(不带/M选项),它将按预期工作。


除非设置 <UseMSDeployExe>true</UseMSDeployExe>,否则在使用VS2017和MsDeploySkipRules时其实并不起作用。 - ceztko

9
Web Deploy如果没有要复制的文件,就不会创建文件夹。在您的情况下,一个解决方法是不使用“从App_Data文件夹中排除文件”复选框,在App_Data中放置一个虚拟文件(例如一个什么也没有的.txt文件),并为App_Data文件夹中的其他内容指定文件排除规则(例如您的.sdf文件)。
关于排除单个文件(可以使用通配符),请参阅MSDN上部署FAQ中的第一个问题:

http://msdn.microsoft.com/en-us/library/ee942158.aspx#can_i_exclude_specific_files_or_folders_from_deployment

使用虚拟文件方法来创建文件夹,请参阅本教程中的确保Elmah文件夹被部署部分。请勿解释,保留HTML标签。

http://www.asp.net/web-forms/tutorials/deployment-to-a-hosting-provider/deployment-to-a-hosting-provider-configuring-project-properties-4-of-12


这似乎应该可以工作,但我已经折腾了一个小时,却没有成功。我可以完成第一部分,但是文件/目录的排除不起作用,无论是否使用通配符。我正在通过CI服务器构建,所以可能是问题所在。我会在接下来的一两天里继续努力,并报告结果。 - Emil Lerch
这种解决第二个问题的方法存在一个缺陷 - 在我的情况下,我想要“排除”在构建时未知的文件(例如带日期的滚动日志文件)。为此,需要使用另一种方法。我会添加一个答案来进一步阐述我发现了什么。 - Emil Lerch
您还可以使用通配符--因此,如果您的日志文件都具有相同的扩展名,则可以排除所有具有该扩展名的文件。 - tdykstra
这是正确的,但前提是日志文件确实被项目文件所知。我在项目中没有包括日志文件进行测试,结果它无法正常工作。 :( - Emil Lerch

7
我成功地使用Visual Studio中的“发布Web”对话框使其工作。注意:它适用于任何文件夹,而不仅仅是App_Data。
以下是基本的.pubxml配置文件:
<?xml version="1.0" encoding="utf-8"?>
<!--
This file is used by the publish/package process of your Web project. You can customize the behavior of this process
by editing this MSBuild file. In order to learn more about this please visit http://go.microsoft.com/fwlink/?LinkID=208121. 
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
<AfterAddIisSettingAndFileContentsToSourceManifest>AddCustomSkipRules</AfterAddIisSettingAndFileContentsToSourceManifest>
    <WebPublishMethod>MSDeploy</WebPublishMethod>
    <LastUsedBuildConfiguration>Local</LastUsedBuildConfiguration>
    <LastUsedPlatform>Any CPU</LastUsedPlatform>
    <SiteUrlToLaunchAfterPublish />
    <ExcludeApp_Data>False</ExcludeApp_Data>
    <MSDeployServiceURL>localhost</MSDeployServiceURL>
    <DeployIisAppPath>SuperCoolAwesomeAppName</DeployIisAppPath>
    <RemoteSitePhysicalPath />
    <SkipExtraFilesOnServer>False</SkipExtraFilesOnServer>
    <MSDeployPublishMethod>InProc</MSDeployPublishMethod>
    <EnableMSDeployBackup>False</EnableMSDeployBackup>
    <UserName />
    <_SavePWD>False</_SavePWD>
    <LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
  </PropertyGroup>

  <PropertyGroup>
    <UseMsDeployExe>true</UseMsDeployExe>
  </PropertyGroup>

  <Target Name="CreateEmptyFolders">
    <Message Text="Adding empty folders to Files" />
    <MakeDir Directories="$(_MSDeployDirPath_FullPath)\Files\Folder 1" />
    <MakeDir Directories="$(_MSDeployDirPath_FullPath)\Files\Folder 2" />
    <MakeDir Directories="$(_MSDeployDirPath_FullPath)\Files\Folder 3\Test"/>
  </Target>

  <Target Name="AddCustomSkipRules" DependsOnTargets="CreateEmptyFolders">
    <Message Text="Adding Custom Skip Rules" />
    <ItemGroup>
      <MsDeploySkipRules Include="SkipFilesInFilesFolder">
        <SkipAction>Delete</SkipAction>
        <ObjectName>filePath</ObjectName>
        <AbsolutePath>$(_DestinationContentPath)\\Files\\.*</AbsolutePath>
        <Apply>Destination</Apply>
      </MsDeploySkipRules>

      <MsDeploySkipRules Include="SkipFoldersInFilesFolders">
        <SkipAction></SkipAction>
        <ObjectName>dirPath</ObjectName>
        <AbsolutePath>$(_DestinationContentPath)\\Files\\.*\\*</AbsolutePath>
        <Apply>Destination</Apply>
      </MsDeploySkipRules>

    </ItemGroup>
  </Target>

</Project>

以下是详细的文章:

使用MsDeploy发布配置文件.pubxml在IIS上创建空文件夹结构,并通过MsDeploySkipRules跳过删除它


0

总结和简化Emil和Leniel的答案,如果你只想允许App_Data部署添加和更新,但防止删除,请将以下内容添加到您的.pubxml文件中。

<Project>
  ...

  <PropertyGroup>
    <UseMSDeployExe>true</UseMSDeployExe>
    <ExcludeApp_Data>False</ExcludeApp_Data>
  </PropertyGroup>

  <Target Name="AddCustomSkipRules"
          AfterTargets="AddIisSettingAndFileContentsToSourceManifest">
    <Message Text="Adding Custom Skip Rules" />
    <ItemGroup>
      <MsDeploySkipRules Include="SkipDeleteAppData">
        <SkipAction>Delete</SkipAction>
        <ObjectName>filePath</ObjectName>
        <AbsolutePath>App_Data\\.*</AbsolutePath>
      </MsDeploySkipRules>
      <MsDeploySkipRules Include="SkipDeleteAppData">
        <SkipAction>Delete</SkipAction>
        <ObjectName>dirPath</ObjectName>
        <AbsolutePath>App_Data</AbsolutePath>
      </MsDeploySkipRules>
    </ItemGroup>
  </Target>
</Project>

<UseMSDeployExe>true</UseMSDeployExe> 是真的需要,否则它会抱怨 Unrecognized skip directive 'skipaction'


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接