Rails6解析远程CSV报Errno::ENOENT无文件错误的解决方法
报错原因
- Ruby内置的
CSV.foreach、未引入扩展库时的原生open、File系列方法,默认仅支持读取本地文件系统路径,不会自动发起HTTP请求拉取远程资源。当你传入HTTP协议的URL时,Ruby会直接把整个URL字符串当作本地文件路径去查找,自然找不到对应文件,因此抛出Errno::ENOENT: No such file or directory错误。 - 直接调用
open(url)报同样错误,是因为Ruby内核原生的open方法没有HTTP协议处理能力,必须引入对应标准库扩展后才能识别远程地址。
正确解析方案
方案1:通过Active Storage原生接口读取(优先推荐)
Active Storage本身提供了附件IO流读取能力,不需要走公网HTTP请求拉取,性能更高,也不受云存储签名URL过期、私有权限配置的影响,是最稳妥的实现方式:
import_job = ImportJob.last # 直接打开附件的IO流交给CSV解析,无需将文件持久化到本地磁盘 import_job.csv_file.open do |io_stream| CSV.parse( io_stream, headers: true, header_converters: :symbol, col_sep: ';' ) do |row| puts row end end
如果处理体积较大的CSV文件,可以搭配Active Storage的分块下载接口使用,避免一次性把整个文件加载到内存造成内存溢出。
方案2:通过远程URL拉取解析
如果业务场景必须使用公开URL读取文件,需要先引入Ruby标准库open-uri,该库会扩展原生open方法,使其支持HTTP/HTTPS协议的资源拉取:
# 必须先引入该标准库,否则open无法识别HTTP地址 require 'open-uri' csv_url = ImportJob.last.csv_file.url # open拉取远程资源后会返回标准IO对象,可直接传给CSV方法解析 CSV.foreach( open(csv_url), headers: true, header_converters: :symbol, col_sep: ';' ) do |row| puts row end
注意:该方案会额外发起一次HTTP请求,性能弱于原生接口读取;如果你的云存储配置了私有读,生成的URL会带签名和过期时间,需要确保解析动作在URL有效期内执行,否则会出现403权限错误。大文件场景不建议使用该方案,容易造成内存占用过高。
内容的提问来源于stack exchange,提问作者David Geismar
相关产品推荐
相关产品推荐

