是否可以从 S3 下载具有数百万行的大文件的前 100 行?
Is it possible to download the first 100 lines of a big file with millions of lines from S3?
我有多个 100MB 的原始文件,其中包含 CSV 格式的一系列用户活动。我只想下载文件的前 100 行。
问题是每个文件可能有不同的 CSV header 列和数据值,因为它们是来自使用不同 activity 跟踪提供商的多个子域的用户活动。这意味着每行可以是50个字符长或500个字符长,直到我全部阅读它们才知道。
S3 支持 getObject
API 和 Range
参数,您可以使用该参数下载文件的特定 XX 字节范围。
https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html#API_GetObject_RequestSyntax
如果我使用这个 API 来解析前 1Mb 的文件,迭代每个字节直到我看到 100 个新行字符 \n
,这在技术上可行吗?对于这种方法,我有什么需要注意的地方吗? (例如多字节字符?)
您可以这样使用 smart_open:
from smart_open import open
with open('s3://bucket/path/file.csv', 'r') as f:
csv_reader = csv.DictReader(f, delimiter=',')
data = ''
for i, row in enumerate(csv_reader):
data += row +'\n'
if i > 100:
store(data)
您需要在本地计算机上打开另一个具有写入权限的文件,以存储 100 行或任意多行。如果您想要来自多个文件的第一行,您可以执行相同的操作,但使用 boto3 函数列出文件并将 path/file 名称发送到使用 smart_open.
的函数
s3client = boto3.client('s3')
listObj = s3client.list_objects_v2(Bucket=bucket, Prefix=prefix)
for obj in listObj['Contents']:
smart_function(obj['Key'])
obj['Key']包含了那个Bucket中每个文件的路径和文件名+Path(Prefix)
没有built-in的方法,byte-range fetches是最好的前进方法。
由于您不确定每种情况下的 header 或行长度,下载 1MB 块直到您有 100 行是一种安全有效的方法。
多字节字符等在这个级别并不重要,您纯粹希望在 100 \n
个字符后停止阅读。但是,根据文件的来源,我也会意识到 \r\n
和 \r
是有效的行结尾。
我写了下面的 Java 代码来获取 last n
字节,请随意使用它作为获取第一个 n
个字节:
public String getLastBytesOfObjectAsString(String bucket, String key, long lastBytesCount) {
try {
final ObjectMetadata objectMetadata = client.getObjectMetadata(bucket, key);
final long fileSizeInBytes = objectMetadata.getContentLength();
long rangeStart = fileSizeInBytes - lastBytesCount;
if (rangeStart < 0) {
rangeStart = 0;
}
final GetObjectRequest getObjectRequest =
new GetObjectRequest(bucket, key).withRange(rangeStart);
try (S3Object s3Object = client.getObject(getObjectRequest);
InputStream inputStream = s3Object.getObjectContent()) {
return new String(inputStream.readAllBytes());
}
} catch (Exception ex) {
...
}
}
我有多个 100MB 的原始文件,其中包含 CSV 格式的一系列用户活动。我只想下载文件的前 100 行。
问题是每个文件可能有不同的 CSV header 列和数据值,因为它们是来自使用不同 activity 跟踪提供商的多个子域的用户活动。这意味着每行可以是50个字符长或500个字符长,直到我全部阅读它们才知道。
S3 支持 getObject
API 和 Range
参数,您可以使用该参数下载文件的特定 XX 字节范围。
https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html#API_GetObject_RequestSyntax
如果我使用这个 API 来解析前 1Mb 的文件,迭代每个字节直到我看到 100 个新行字符 \n
,这在技术上可行吗?对于这种方法,我有什么需要注意的地方吗? (例如多字节字符?)
您可以这样使用 smart_open:
from smart_open import open
with open('s3://bucket/path/file.csv', 'r') as f:
csv_reader = csv.DictReader(f, delimiter=',')
data = ''
for i, row in enumerate(csv_reader):
data += row +'\n'
if i > 100:
store(data)
您需要在本地计算机上打开另一个具有写入权限的文件,以存储 100 行或任意多行。如果您想要来自多个文件的第一行,您可以执行相同的操作,但使用 boto3 函数列出文件并将 path/file 名称发送到使用 smart_open.
的函数s3client = boto3.client('s3')
listObj = s3client.list_objects_v2(Bucket=bucket, Prefix=prefix)
for obj in listObj['Contents']:
smart_function(obj['Key'])
obj['Key']包含了那个Bucket中每个文件的路径和文件名+Path(Prefix)
没有built-in的方法,byte-range fetches是最好的前进方法。
由于您不确定每种情况下的 header 或行长度,下载 1MB 块直到您有 100 行是一种安全有效的方法。
多字节字符等在这个级别并不重要,您纯粹希望在 100 \n
个字符后停止阅读。但是,根据文件的来源,我也会意识到 \r\n
和 \r
是有效的行结尾。
我写了下面的 Java 代码来获取 last n
字节,请随意使用它作为获取第一个 n
个字节:
public String getLastBytesOfObjectAsString(String bucket, String key, long lastBytesCount) {
try {
final ObjectMetadata objectMetadata = client.getObjectMetadata(bucket, key);
final long fileSizeInBytes = objectMetadata.getContentLength();
long rangeStart = fileSizeInBytes - lastBytesCount;
if (rangeStart < 0) {
rangeStart = 0;
}
final GetObjectRequest getObjectRequest =
new GetObjectRequest(bucket, key).withRange(rangeStart);
try (S3Object s3Object = client.getObject(getObjectRequest);
InputStream inputStream = s3Object.getObjectContent()) {
return new String(inputStream.readAllBytes());
}
} catch (Exception ex) {
...
}
}