从 ASP.NET Core 应用程序访问其他服务器上的共享文件夹(网络连接程序版本)

更新页 :
页面创建日期 :

操作验证环境

Visual 工作室
  • Visual Studio 2022
ASP.NET 核心
  • 8 (Razor Pages, MVC)
Windows 服务器
  • 2022 (ASP.NET 核心系统要求)
  • 2019(共享文件夹部署服务器)
IIS的
  • 10.0

操作环境

我们尚未在所有情况下对其进行测试,但它应该在以下条件下正常工作:

Visual 工作室
  • 任何可以开发 ASP.NET 核心项目的东西
ASP.NET 核心
  • 任何版本(MVC、Razor Pages、API)
Windows 服务器
  • Windows Server 2008 或更高版本
IIS的
  • 7.0 或更高版本

前提

  • ASP.NET Core 应用程序旨在在 IIS 上运行。
  • 由于它使用 Windows API 进行身份验证,因此它不适用于非 Windows。

环境

在以下环境中验证。

PC和服务器 的使用目的
Windows 11(本地) 开发程序的环境。
SV2022测试 运行 IIS 和 ASP.NET Core 的环境。 从此处访问 SV2019Test 共享文件夹
SV2019测试 具有共享文件夹的服务器

此外,各种设置如下。

参数名称
访问用户名 共享用户
共享文件夹名称 SharedFolder

构建共享文件夹服务器

创建用户

创建用户以访问公共文件夹。 在这种情况下,我们将创建一个本地帐户,但如果您正在处理域(如 Active Directory)中的服务器和帐户,您也可以使用它。

创建用户的过程超出了这些提示的范围,因此我们不会过多地介绍。

SharedUser在本例中,我们将使用名称 创建它。 由于此用户不操作屏幕或更改设置,因此无法更改密码。

如果保留默认值,则可以使用远程桌面等使用此用户登录,因此请从组 Users 中删除。

创建共享文件夹

在哪里创建它并不重要。 这是因为其他服务器不关心物理文件夹的位置。 在这种情况下,我们将创建一个直接在 C 盘下 SharedFolder 命名的文件夹并共享它。

打开属性并配置共享设置。

共享文件夹的名称应 SharedFolder 为 。 此名称将对其他服务器可见。 添加 SharedUser 权限。

Everyone删除现有的 .

使用“更改”权限进行确认。

由于我们只添加了可以从外部访问的权限,因此我们将在内部 SharedUser 设置它,以便可以在此文件夹中运行。

使用“更改”权限进行确认。

创建一个文件来检查操作。 在此程序中,编码是用 UFT-8 处理的,因此请将其保存为 UTF-8。

如果可以从另一台 PC 访问资源管理器 \\<サーバー名>\SharedUser 使用 登录并查看文件,则可以。

创建一个程序以从 ASP.NET Core 应用程序从共享文件夹读取和写入文件

Mr./Ms. 操作是单击按钮。

  • 将文件加载到共享文件夹中并在屏幕上显示它们
  • 将新文件写入共享文件夹

过程。

Razor Pages 和 MVC 代码是这方面的示例,但访问共享文件夹的程序对于两者是相同的。 Web API 也是如此。 如果有的话,它也应该在客户端程序中工作。

在特定进程期间连接到网络的进程

在项目中的任意位置创建以下代码。 类名是 SharedFolderAccessor ,但名称是任意的。 SharedFolderAccessor 在创建 Dispose 的实例之前,您可以访问共享文件夹。 using 允许您指定可以在显式范围内访问访问的时间段。

using System.ComponentModel;
using System.Net;
using System.Runtime.InteropServices;

/// <summary>
/// 共有フォルダにユーザー名とパスワードでアクセスするためのクラスです。
/// using を使用すればそのスコープの間、共有フォルダにアクセスできます。
/// </summary>
public class SharedFolderAccessor : IDisposable
{
  private readonly string _networkName;

  /// <summary>
  /// コンストラクタです。
  /// </summary>
  /// <param name="networkName">共有フォルダのあるサーバーを「\\&lt;サーバー名&gt;」形式で指定します。</param>
  /// <param name="credentials">共有フォルダにアクセスするための資格情報です。</param>
  /// <exception cref="Win32Exception"></exception>
  public SharedFolderAccessor(string networkName, NetworkCredential credentials)
  {
    _networkName = networkName;

    // 接続するネットワークの情報を設定
    var netResource = new NetResource
    {
      Scope = ResourceScope.GlobalNetwork,
      ResourceType = ResourceType.Disk,
      DisplayType = ResourceDisplaytype.Share,
      RemoteName = networkName,
    };

    // ドメインがある場合はドメイン名も指定、ない場合はユーザー名のみ
    var userName = string.IsNullOrEmpty(credentials.Domain)
        ? credentials.UserName
        : $@"{credentials.Domain}\{credentials.UserName}";

    // 共有フォルダにユーザー名とパスワードで接続
    var result = WNetAddConnection2(netResource, credentials.Password, userName, 0);

    if (result != 0)
    {
      throw new Win32Exception(result, $"共有フォルダに接続できませんでした。(エラーコード:{result})");
    }

    // 正常に接続できれば WNetCancelConnection2 を呼び出すまではプログラムで共有フォルダにアクセス可能
  }

  ~SharedFolderAccessor()
  {
    // Dispose を呼び忘れたときの保険
    WNetCancelConnection2(_networkName, 0, true);
  }

  public void Dispose()
  {
    WNetCancelConnection2(_networkName, 0, true);
    GC.SuppressFinalize(this);  // Dispose を明示的に呼んだ場合はデストラクタの処理は不要
  }

  /// <summary>
  /// ネットワーク リソースへの接続を確立し、ローカル デバイスをネットワーク リソースにリダイレクトできます。
  /// </summary>
  /// <param name="netResource">ネットワーク リソース、ローカル デバイス、ネットワーク リソース プロバイダーに関する情報など。</param>
  /// <param name="password">ネットワーク接続の作成に使用するパスワード。</param>
  /// <param name="username">接続を確立するためのユーザー名。</param>
  /// <param name="flags">接続オプションのセット。</param>
  /// <returns></returns>
  [DllImport("mpr.dll")]
  private static extern int WNetAddConnection2(NetResource netResource, string password, string username, int flags);

  /// <summary>
  /// 既存のネットワーク接続を取り消します。
  /// </summary>
  /// <param name="name">リダイレクトされたローカル デバイスまたは切断するリモート ネットワーク リソースの名前。</param>
  /// <param name="flags">接続の種類。</param>
  /// <param name="force">接続に開いているファイルまたはジョブがある場合に切断を行う必要があるかどうか。</param>
  /// <returns></returns>
  [DllImport("mpr.dll")]
  private static extern int WNetCancelConnection2(string name, int flags, bool force);

  /// <summary>
  /// NETRESOURCE 構造体を定義しています。
  /// </summary>
  [StructLayout(LayoutKind.Sequential)]
  private class NetResource
  {
    public ResourceScope Scope;
    public ResourceType ResourceType;
    public ResourceDisplaytype DisplayType;
    public int Usage;
    public string LocalName = "";
    public string RemoteName = "";
    public string Comment = "";
    public string Provider = "";
  }

  /// <summary>
  /// ネットワークリソースのスコープ。
  /// </summary>
  private enum ResourceScope : int
  {
    /// <summary>ネットワークリソースへの現在の接続。</summary>
    Connected = 1,
    /// <summary>すべてのネットワークリソース。</summary>
    GlobalNetwork = 2,
    Remembered = 3,
    Recent = 4,
    /// <summary>ユーザーの現在および既定のネットワークコンテキストに関連付けられているネットワークリソース。</summary>
    Context = 5,
  };

  /// <summary>
  /// リソースの種類。
  /// </summary>
  private enum ResourceType : int
  {
    /// <summary>印刷リソースとディスクリソースの両方のコンテナー、または印刷またはディスク以外のリソースなど。</summary>
    Any = 0,
    /// <summary>共有ディスクボリューム。</summary>
    Disk = 1,
    /// <summary>共有プリンター。</summary>
    Print = 2,
    Reserved = 8,
  }

  /// <summary>
  /// ユーザーインターフェイスで使用する必要がある表示の種類。
  /// </summary>
  private enum ResourceDisplaytype : int
  {
    /// <summary>リソースの種類を指定しないネットワークプロバイダーによって使用されます。</summary>
    Generic = 0x0,
    /// <summary>サーバーのコレクション。</summary>
    Domain = 0x01,
    /// <summary>サーバー。</summary>
    Server = 0x02,
    /// <summary>共有ポイント。</summary>
    Share = 0x03,
    File = 0x04,
    Group = 0x05,
    /// <summary>ネットワークプロバイダー。</summary>
    Network = 0x06,
    Root = 0x07,
    Shareadmin = 0x08,
    /// <summary>ディレクトリ。</summary>
    Directory = 0x09,
    Tree = 0x0a,
    Ndscontainer = 0x0b,
  }
}

由于它使用 Win32 API 和 WNetAddConnection2 WNetCancelConnection2 ,因此它仅在 Windows 环境中工作。 我暂时有评论,但如果您想了解更多,请在互联网上查找。 除了共享文件夹外,您还可以执行访问网络资源的操作。

它易于使用,如果您编写以下内容,则 using 可以在作用域期间访问共享文件夹。

using (new SharedFolderAccessor($@"\\{serverName}", credentials))
{
  // この間は共有フォルダにアクセスできる
}

但是,实际上 WNetCancelConnection2 ,在调用时不会立即断开连接,因此 using 即使在作用域之后也可以访问共享文件夹。

使用 SharedFolderAccessor 测试代码

由于访问共享文件夹的过程不依赖于框架,因此我们创建了一种用于读取和写入文件的通用测试方法。 Pazor Pages 和 MVC 应该被称为相同的。

内容是一个进程,该过程将传递给参数的文本写入共享文件夹,读取共享文件夹中已有的文本文件,并返回文本。

using System.Net;

public static class Util
{
  public static string ReadAndWrite(string text)
  {
    var serverName = "ServerName";
    var folderName = "SharedFolder";
    var inputFileName = "Input.txt";
    var outputFileName = "Output.txt";
    var username = "SharedUser";
    var password = "password";

    var credentials = new NetworkCredential(username, password);
    using (new SharedFolderAccessor($@"\\{serverName}", credentials))
    {
      // ファイルの書き出し
      System.IO.File.WriteAllText(Path.Combine($@"\\{serverName}\{folderName}", outputFileName), text);

      // ファイルの読み込み
      return System.IO.File.ReadAllText(Path.Combine($@"\\{serverName}\{folderName}", inputFileName));
    }
  }
}

Razor 页面

在 Razor Pages 中,我们在 Index.cshtml 中放置一个按钮,单击它,运行测试代码,然后在屏幕上显示结果。 在某些情况下,共享文件夹可能无法访问,因此将其包含在 try-catch 中。

Index.cshtml.cs

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace SharedFolderAccessRazorPages.Pages
{
  public class IndexModel : PageModel
  {
    private readonly ILogger<IndexModel> _logger;
    public string Message { get; set; } = "";

    public IndexModel(ILogger<IndexModel> logger)
    {
      _logger = logger;
    }

    public void OnPost()
    {
      try
      {
        Message = Util.ReadAndWrite($"プログラムからの書き込み ({DateTime.Now})");
      }
      catch (Exception ex)
      {
        Message = ex.ToString();
      }
    }
  }
}

索引.cshtml

@page
@model IndexModel
@{
  ViewData["Title"] = "Home page";
}

<div class="text-center">
  <h1 class="display-4">Welcome</h1>
  <p>Learn about <a href="https://learn.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>

@* ここから追加 *@
<form method="post">
  <button type="submit">サブミット</button>
</form>

<div>
  @Model.Message
</div>
@* ここまで追加 *@

MVC的

同样,在 MVC 中, Index.cshtml 将一个按钮放在 中,单击时,它会调用共享文件夹的测试代码。

控制器/HomeController.cs

// 省略

namespace SharedFolderAccessMvc.Controllers
{
  public class HomeController : Controller
  {
    // 省略

    public IActionResult Index()
    {
      return View();
    }

    // ここから追加
    [HttpPost]
    public IActionResult Index(string dummy)
    {
      try
      {
        ViewData["Message"] = Util.ReadAndWrite($"プログラムからの書き込み ({DateTime.Now})");
      }
      catch (Exception ex)
      {
        ViewData["Message"] = ex.ToString();
      }
      return View();
    }
    // ここまで追加

    public IActionResult Privacy()
    {
      return View();
    }

    // 省略
  }
}

索引.cshtml

@{
  ViewData["Title"] = "Home Page";
}

<div class="text-center">
  <h1 class="display-4">Welcome</h1>
  <p>Learn about <a href="https://learn.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>

@* ここから追加 *@
<form method="post">
  <button type="submit">サブミット</button>
</form>

<div>
  @ViewData["Message"]
</div>
@* ここまで追加 *@

操作确认

调试并验证是否可以成功访问共享文件夹。

构建应用程序服务器

由于我们已经确认可以通过运行程序访问共享文件夹,因此无需进行下一步。 如果要检查IIS服务器上的操作,可以按照以下步骤进行操作。

从这里开始,它是一个补充,所以我不会解释它太详细,而是主要解释图像。

安装 IIS

默认情况下,从服务器管理器安装它。 我不会详细介绍该过程。

不需要其他功能。

目前不需要其他 IIS 服务。

ASP.NET Core Runtime Hosting Bundle 安装

由于我们使用的是 Core 8 ASP.NET 因此我们需要相应地安装运行时。 从以下 URL 下载:

为了在IIS中运行 ASP.NET Core,您需要称为“Hosting Bundle”的东西。 从 ASP.NET Core 运行时下载“Hosting Bundle”。

下载后,在服务器上运行它。

按照向导进行安装。

发布程序

将要部署到 IIS 的程序作为文件从 Visual Studio 输出。

针对 Windows 修改它。

完成后发布。

如果单击目标位置,则可以打开已发布文件所在的文件夹。

你不必把它们都带上,但如果你不确定,你可以暂时把它们都带上。 此时,您需要做的就是确保文件存在。

创建和部署 Web 应用程序

在 Windows 管理工具中,打开 Internet Information Services (IIS) 管理器。

我们将创建一个站点,但这次我们将使用从一开始就存在的“默认网站”。

选择“默认网站”后,单击“资源管理器”打开文件夹。 在此处复制已发布的程序。 您可以删除原始文件。

从 IIS 链接打开页面,查看屏幕是否显示。 您可以先打开 Web 浏览器并直接输入 URL。

操作确认

单击该按钮以验证其是否正常工作。 在上面,它是从Web服务器内部访问的,但是由于它是Web服务器,因此应该可以从其他PC操作它。