使用 R 读取以 "smb://" 开头的远程文件

Read remote file beginning with "smb://" using R

要读取 R 中的文件,我通常会执行如下操作:

read.csv('/Users/myusername/myfilename.csv')

但是,我正在尝试读取位于远程服务器(Windows SMB/CIFS 共享)上的文件,我通过 在 Mac 上访问该文件FinderGoConnect to Server 菜单项。

当我查看那个文件的属性时,文件路径与我习惯的不同。不是以 /Users/myusername/... 开头,而是 smb://server.msu.edu/.../myfilename.csv.

尝试读取文件,我尝试了以下操作:

read.csv('smb://server.msu.edu/.../myfilename.csv')

但是,这没有用。

而不是通常的 "No such file or directory" 错误,返回的是:

smb://server.msu.edu/.../myfilename.csv does not exist in current working directory

我想文件路径需要不同的格式,但我不知道是什么。

如何在 R 中读取这种类型的文件?

SMB 是 Windows 网络文件夹协议。

例如,类似的情况包括 sftp:// 个网址。

您可以:

  1. 在您的操作系统中安装该文件夹,并使用常规路径访问它,
  2. 使用虚拟文件系统库,例如 GVFS/GIO on Linux。也许有一些 R 包装器可以使用。

下面我展示了一种我不时使用的从 SMB 网络驱动器读取数据的方法。在下面的代码中,我使用 R system 函数在 R 中执行所有操作,但您也可以从 OSX 命令行或在 Finder 中使用 Command-K(连接到服务器):

如果您还没有,请在共享所在的本地驱动器上创建一个目录(这不是必需的,因为您可以将驱动器安装在现有位置):

system("mkdir /Users/eipi10/temp_share/")

dir.create("/Users/eipi10/temp_share/")

将网络驱动器安装到您刚刚创建的文件夹中。在下面的代码中,//username@domain.address.edu/home/u/eipi10 是您的用户名和 SMB 共享的地址。

system("mount_smbfs //username@domain.address.edu/home/u/eipi10 /Users/eipi10/temp_share")

如果有密码认证,那么也可以加上密码:

system("mount_smbfs //username:password@domain.address.edu/home/u/eipi10 /Users/eipi10/temp_share")

读取数据:

dat = read.csv("/Users/eipi10/temp_share/fileToRead.csv")

在 R 中,您还可以通过编程方式 select 文件来读取:

data.list = lapply(list.files(pattern="csv$", "/Users/eipi10/temp_share/", full.names=TRUE), read.csv)

说明

smb://educ-srvmedia1.campusad.msu.edu/... 实际上是 URL 而不是文件路径。

我们来分解一下

smb://表示使用server message block protocol(文件共享)

educ-srvmedia1.campusad.msu.edu 是服务器的名称

/.../myfilename.csv 是远程服务器

上的文件 share/path

您可以使用 OSX 上的 Finder 导航到此目录,因为它内置了对 SMB 协议的支持。 Finder 使用 URL 连接到远程服务并允许您浏览文件。

但是 R 不了解 SMB 协议,因此无法正确解释文件路径。

R 函数 read.csv() 在内部使用 file(),参见 https://stat.ethz.ch/R-manual/R-devel/library/base/html/connections.html

url and file support URL schemes file://, http://, https:// and ftp://

因此 R returns "unable to locate the file" 消息因为无法找到文件,因为协议不受支持。是的,有点混乱。

修复

您需要在本地文件系统上安装文件共享。

这意味着 SMB 协议的细节将由 OS 在幕后处理,文件共享将作为本地目录显示。

这将允许 R(和其他程序)出于所有目的和目的处理远程文件,就像任何其他本地文件一样。 This discussion 显示了一些这样做的选项。

例如

# need to create /LocalFolder first
mount -t cifs //username:password@hostname/sharename /LocalFolder

然后在 R 中:

read.csv('/LocalFolder/myfilename.csv')

额外

Windows 用户可以使用 UNC 路径更轻松地完成此操作
How to read files from a UNC-specified directory in R?

在我看来,有两种方法可以实现您的目标。

  • 第一个是使用fstab 将远程文件夹明确添加为本地磁盘

  • 第二个是需要的时候临时挂载远程文件夹作为文件夹


下面,我将解释第二种方法是如何实现的

  • 创建本地目录:

    mkdir <mountdirectory>

  • 使用此命令行将远程目录挂载到本地:

    sshfs <remoteserverip>:<remotedirpath> <mountdirectory> 用于 SSH

    (先安装cifs util:sudo apt-get install cifs-utils

    mount -t cifs -o username=<USERNAME>,password=<PASSWD> //<remoteserverip>/<remotedirpath> <mountdirectory> 对于 SMB

  • 用本地文件完成工作 !

  • 最后,使用这个命令卸载:

    fusermount -u <mountdirectory>

TL;DR

这是一种使用 cURL 并且不需要安装远程文件系统的可移植方法:

> install.packages("curl")
> require("curl")
> handle <- new_handle()
> handle_setopt(handle, username = "domain\username")
> handle_setopt(handle, password = "secret") # If needed
> request <- curl_fetch_memory("smb://host.example.com/share/file.txt", handle = handle)
> contents <- rawToChar(request$content)

如果我们需要以 CSV 格式读取内容,就像问题中那样,我们可以通过另一个函数流式传输文件:

> stream <- curl("smb://host.example.com/share/file.txt", handle = handle)
> contents <- read.csv(stream)

除了其他答案中描述的安装远程文件系统的方法之外,让我们看看通过 smb:// URL 访问远程文件的更可靠的方法。不幸的是,我来晚了一点,但我希望这对未来的读者有所帮助。

在某些情况下,我们可能没有挂载文件系统所需的权限(在许多系统上这需要管理员或 root 访问权限),或者我们可能根本 不想 挂载整个文件系统只是为了读取单个文件。我们将使用 cURL library 来读取文件。这种方法提高了我们程序的灵活性和可移植性,因为我们不需要依赖外部挂载文件系统的存在。我们将研究两种不同的方法:通过 system() 调用,以及使用提供 cURL API.

的包

一些背景知识:对于那些不熟悉它的人,cURL 提供了用于通过各种协议传输数据的工具。自 7.40 版以来,cURL 支持通常用于 Windows 文件共享服务的 SMB/CIFS 协议。 cURL 包含 a command-line tool,我们可以使用它来获取文件的内容:

$ curl -u 'domain\username' 'smb://host.example.com/share/file.txt'

上面的命令从远程服务器host.example.com[=104读取并输出(到STDOUT)file.txt的内容=] 作为域上的指定用户进行身份验证。如果需要,该命令将提示我们输入密码。如果我们的网络不使用域,我们可以从用户名中删除域部分。

系统调用

我们可以使用 system() function 在 R 中实现相同的功能:

system("curl -u 'domain\username' 'smb://host.example.com/share/file.txt'")

注意 domain\username 中的双反斜杠。这会转义反斜杠字符,以便 R 不会将其解释为字符串中的转义字符。我们可以通过将 system() 函数的 intern 参数设置为 TRUE:

来将命令输出中的文件内容捕获到一个变量中
contents <- system("curl -u 'domain\username' 'smb://host.example.com/share/file.txt'", intern = TRUE)

...或者通过调用 system2() 来代替,它引用命令参数以确保安全并更好地处理平台之间的进程重定向:

contents <- system2('curl', c("-u", "domain\\username", "smb://host.example.com/share/file.txt"), stdout = TRUE)

如果远程服务器需要,curl 命令仍会提示我们输入密码。虽然我们 可以 使用 -u 'domain\username:password' 指定密码来避免提示,但这样做会在命令字符串中暴露明文密码。要获得更安全的方法,请阅读下面描述包用法的部分。

我们也可以在curl命令中添加-s--silent标志来抑制进度状态输出。请注意,这样做也会隐藏错误消息,因此我们可能还想添加 -S (--show-error)。 contents 变量将包含文件行的向量——类似于 readLines("file.txt") 返回的值——我们可以使用 paste(contents, collapse = "\n").

将其压缩在一起

cURL API

虽然这一切都很好,但我们可以通过使用专用的 cURL 库来改进这种方法。这 curl package provides R bindings to libcurl 这样我们就可以在我们的程序中直接使用 cURL API 了。首先我们需要安装包:

install.packages("curl")
require("curl")

(Linux 用户需要 install libcurl development files。)

然后,我们可以使用curl_fetch_memory()函数将远程文件读入一个变量:

handle <- new_handle()
handle_setopt(handle, username = "domain\username")
handle_setopt(handle, password = "secret") # If needed
request <- curl_fetch_memory("smb://host.example.com/share/file.txt", handle = handle)
content <- rawToChar(request$content)

首先,我们创建一个 handle 以通过设置所需的任何身份验证选项来配置请求。然后,我们执行请求并将文件的内容分配给一个变量。如图所示,根据需要设置 password CURLOPT。

要像 read.csv() 一样处理远程文件,我们需要创建一个流连接。 curl() 函数创建一个连接对象,我们可以使用它通过任何支持标准 url() 函数返回的参数的函数来流式传输文件内容。例如,这里有一种将远程文件读取为 CSV 的方法,如问题:

handle = new_handle()
...
stream <- curl("smb://host.example.com/share/file.txt", handle = handle)
contents <- read.csv(stream)

当然,上述概念适用于通过 cURL 支持的任何协议 获取内容或响应主体,而不仅仅是 SMB/CIFS。如果需要,我们还可以使用这些工具将文件下载到文件系统,而不仅仅是将内容读入内存。