在 Java 中将文件的前 N ​​个字节作为输入流读取?

2024-01-02

在我的一生中,我一直无法找到与我想做的事情相匹配的问题,所以我将在这里解释我的用例。如果您知道某个主题已经涵盖了该问题的答案,请随时引导我找到该主题。 :)

我有一段代码可以定期(每 20 秒)将文件上传到 Amazon S3。该文件是由另一个进程写入的日志文件,因此此函数实际上是一种跟踪日志的方法,以便某人可以半实时读取其内容,而无需直接访问日志所在的计算机。

直到最近,我只是使用 S3 PutObject 方法(使用文件作为输入)来执行此上传。但在 AWS SDK 1.9 中,这不再有效,因为如果实际上传的内容大小大于上传开始时承诺的内容长度,S3 客户端会拒绝请求。此方法在开始流式传输数据之前读取文件的大小,因此考虑到此应用程序的性质,文件的大小很可能在该点和流结束之间增加。这意味着我现在需要确保只发送 N 字节的数据,无论文件有多大。

我不需要以任何方式解释文件中的字节,所以我不关心编码。我可以逐字节传输它。基本上,我想要的是一种简单的方法,我可以读取文件直至第 N 个字节,然后让它终止读取,即使文件中还有更多数据超过该点。 (换句话说,将 EOF 插入流中的特定点。)

例如,如果我的文件在开始上传时为 10000 字节长,但在上传过程中增长到 12000 字节,则无论大小如何变化,我都希望在 10000 字节时停止上传。 (在后续上传时,我会上传 12000 字节或更多。)

我还没有找到一种预先制作的方法来做到这一点 - 到目前为止我发现的最好的方法似乎是 IOUtils.copyLarge(InputStream, OutputStream, offset, length),它可以被告知复制最大“长度”字节到提供的 OutputStream。然而,copyLarge 是一种阻塞方法,PutObject 也是如此(它可能在其 InputStream 上调用一种形式的 read()),所以看来我根本无法让它工作。

我还没有找到任何方法或预构建的流可以做到这一点,所以这让我觉得我需要编写自己的实现来直接监视已读取的字节数。这可能会像 BufferedInputStream 一样工作,其中每批读取的字节数是缓冲区大小或要读取的剩余字节中的较小者。 (例如,如果缓冲区大小为 3000 字节,我会执行三个批次,每个批次为 3000 字节,然后是一个批次为 1000 字节 + EOF。)

有谁知道更好的方法来做到这一点?谢谢。

EDIT只是为了澄清一下,我已经知道了几种替代方案,但它们都不理想:

(1)我可以在上传文件时锁定文件。这样做会导致写入文件的过程中数据丢失或操作问题。

(2) 我可以在上传文件之前创建文件的本地副本。这可能效率非常低,并且占用大量不必要的磁盘空间(该文件可能会增长到几 GB 范围,并且运行它的计算机可能磁盘空间不足)。

EDIT 2:根据同事的建议,我的最终解决方案如下所示:

private void uploadLogFile(final File logFile) {
    if (logFile.exists()) {
        long byteLength = logFile.length();
        try (
            FileInputStream fileStream = new FileInputStream(logFile);
            InputStream limitStream = ByteStreams.limit(fileStream, byteLength);
        ) {
            ObjectMetadata md = new ObjectMetadata();
            md.setContentLength(byteLength);
            // Set other metadata as appropriate.
            PutObjectRequest req = new PutObjectRequest(bucket, key, limitStream, md);
            s3Client.putObject(req);
        } // plus exception handling
    }
}

LimitInputStream 是我的同事建议的,显然不知道它已被弃用。 ByteStreams.limit 是当前 Guava 的替代品,它可以满足我的需求。感谢大家。


完整答案删除和替换:

包裹起来相对简单InputStream例如在发出数据结束信号之前限制其将传递的字节数。FilterInputStream是针对这种一般类型的工作,但是由于您必须为此覆盖几乎所有方法特别的工作,它只是妨碍。

这是一个粗略的解决方案:

import java.io.IOException;
import java.io.InputStream;

/**
 * An {@code InputStream} wrapper that provides up to a maximum number of
 * bytes from the underlying stream.  Does not support mark/reset, even
 * when the wrapped stream does, and does not perform any buffering.
 */
public class BoundedInputStream extends InputStream {

    /** This stream's underlying @{code InputStream} */
    private final InputStream data;

    /** The maximum number of bytes still available from this stream */ 
    private long bytesRemaining;

    /**
     * Initializes a new {@code BoundedInputStream} with the specified
     * underlying stream and byte limit
     * @param data the @{code InputStream} serving as the source of this
     *        one's data
     * @param maxBytes the maximum number of bytes this stream will deliver
     *        before signaling end-of-data
     */
    public BoundedInputStream(InputStream data, long maxBytes) {
        this.data = data;
        bytesRemaining = Math.max(maxBytes, 0);
    }

    @Override
    public int available() throws IOException {
        return (int) Math.min(data.available(), bytesRemaining);
    }

    @Override
    public void close() throws IOException {
        data.close();
    }

    @Override
    public synchronized void mark(int limit) {
        // does nothing
    }

    @Override
    public boolean markSupported() {
        return false;
    }

    @Override
    public int read(byte[] buf, int off, int len) throws IOException {
        if (bytesRemaining > 0) {
            int nRead = data.read(
                    buf, off, (int) Math.min(len, bytesRemaining));

            bytesRemaining -= nRead;

            return nRead;
        } else {
            return -1;
        }
    }

    @Override
    public int read(byte[] buf) throws IOException {
        return this.read(buf, 0, buf.length);
    }

    @Override
    public synchronized void reset() throws IOException {
        throw new IOException("reset() not supported");
    }

    @Override
    public long skip(long n) throws IOException {
        long skipped = data.skip(Math.min(n, bytesRemaining));

        bytesRemaining -= skipped;

        return skipped;
    }

    @Override
    public int read() throws IOException {
        if (bytesRemaining > 0) {
            int c = data.read();

            if (c >= 0) {
                bytesRemaining -= 1;
            }

            return c;
        } else {
            return -1;
        }
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Java 中将文件的前 N ​​个字节作为输入流读取? 的相关文章

随机推荐

  • 制作键盘记录器

    我想在我自己的电脑上制作一个小型键盘记录器 以了解击键如何与 C 配合使用 我在网上找到了一些代码 只是对其进行了一些编辑 尽管我不确定如何做我想做的事情 include stdafx h include
  • 如何在iOS自动布局中为按钮添加等间距和等宽度

    我对 Xcode 中的自动布局约束不熟悉 我有一个像这样的底视图UITabBar with 6 UIButtons 在没有限制的情况下 我将这些按钮与每个按钮的 5 个空格填充对齐 并且每个按钮的宽度为 50 现在 我试图通过使用自动布局来
  • 根据“.”将GL_CODE列拆分为不同的列,而不使用SQL中的函数[重复]

    这个问题在这里已经有答案了 我需要一个查询来根据 将 GL CODE 分成不同的列 作为分隔符 我已附上我已完成的设置的屏幕截图 但在提取第七段后出现错误 请注意 每个元素中的每行的字符串长度并不相同 我使用过的代码 select YEAR
  • 是否有任何工具可以将 SVG 数据转换为 Canvas 友好的输入? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 如何在 android 中限制仅沿 y 轴拖放?

    我试图将拖放运动限制为仅在 Y 轴上 以便用户只能查看视图并将其向上或向下拖动 而不能向左或向右拖动 我现在有两个视图 textView 和 dropZone 的 id 其中一个 textView 设置了触摸侦听器 另一个 dropZone
  • 自上而下的递归方案

    我们能否定义一个递归方案 在不失去任何通用性的情况下 自上而下构造值 而不是自下而上 这将非常有帮助 因为我已经看到很多次首先应用使用递归方案内部定义的函数reverse其输入 清楚地表明需要foldl 就像 从前到后 执行 尽管人们普遍认
  • 经典 ASP - 获取完整的 url 名称

    我想知道是否有人可以帮助我 我有以下 URL 这是动态的 www website com images gal boxes pic004 asp 我如何使用经典 ASP 提取 boxes pic004 部分 Thanks
  • 返回应用程序时取消选择表视图行

    我有一个表格视图 其中一个表格视图单元格打开另一个应用程序 当我返回我的应用程序时 表格视图单元格仍然突出显示 返回应用程序时取消选择表格视图单元格的最佳方法是什么 编辑 问题是 viewWillAppear or viewDidAppea
  • 如何为 UITextField 实现类似 NSLineBreakByTruncatingHead 的功能?

    我需要实现类似的目标NSLineBreakByTruncatingHead for UITextField如此处所示 我们假设原文是 这是无法在 UITextField 内显示的长文本 我需要它像 无法显示在 UITextField 内 但
  • 在 Objective C (Cocoa) 线程中运行 C 代码(适用于 iOS)

    首先 我是一名非常新的 Objective C Cocoa iOS 开发人员 但我以前编写过 C C 应用程序 因此 我设法在我的 iPhone 应用程序中运行 Rabbitmq c http hg rabbitmq com rabbitm
  • Java中数组的使用方法

    我是 Java 新手 我正在阅读如何在 java 中使用数组 它说在java中使用数组你必须遵循三个步骤 即声明数组 创建数组和初始化数组 我明白了但是当我没有遵循上面的所有步骤时 下面的代码如何工作 int array 1234 234
  • 正确保存并更新单选按钮响应 java

    我正在尝试将单选按钮用户响应保存在 Firestore 中的 UID 下 我有两个选择yes and no到这个问题 它仅在用户按下按钮选择一个选项时起作用一次 但如果用户想要更改答案 它不会更新 替换旧响应 我想知道是否有人可以提供帮助
  • Python 长文件名支持在 Windows 中被破坏

    我编写Python脚本来复制文件 不幸的是 由于文件名太长 gt 256 它一直失败 有办法解决这个问题吗 我使用的是 Python 2 5 4 和 Windows XP Cheers Use 以字符串开头的路径 http msdn mic
  • IPv4 和 IPv6 禁止

    如果我想在我的网站上通过 IP 禁止用户 是否可以通过两者来实现IPv4 and IPv6 某些浏览器显然默认使用 IPv4 地址 而其他浏览器 如果有可能 则使用 IPv6 地址 因此 如果我通过某人当前的 IP 对其进行禁止 他们只需使
  • 解决MultisampleFramebufferAPPLE生成INVALID_OPERATION

    我不明白为什么glResolveMultisampleFramebufferAPPLE生成错误 1282 0x0502 GL INVALID OPERATION 设置代码 glGenFramebuffers 1 framebuffer gl
  • 为现有基于 MVC 的网站创建 REST API

    我有一个使用 ASP NET MVC3 开发的网站 我现在想公开一个 REST API 供其他人使用 它将公开与网站相同的功能 在网站中 一旦用户登录并根据数据库验证凭据 会话就会管理用户的登录状态 我如何使用 REST API 执行相同的
  • 在 PHP 中使用 getter 和 setter 代替函数或简单的公共字段有什么优点? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 我不是 PHP 开发人员 所以我想知道 PHP 中以纯 OOP 风格使用私有字段 我喜欢的方式 使用显式 getter setter 的优点和缺
  • 保证金不起作用?两个元素之间需要空间

    首先 我很抱歉我没有把链接放在这里 这是一个工作网站 我不被允许 如果有必要 我会发布我的代码的相关部分 所以问题是非常基本的 我有一个带有一些图像的 div 和一个标题 h3 下面是我的内容开始的地方 无论我如何努力在两者之间创造一些空间
  • 在 if 语句/管理进程中使用 fork

    我有这段代码 printf L1 if fork 0 printf L2 if fork 0 printf L3 fork printf End n 作为练习 我试图找出运行此代码 而不实际运行它 所产生的有效 无效输出的一些示例 我仍然对
  • 在 Java 中将文件的前 N ​​个字节作为输入流读取?

    在我的一生中 我一直无法找到与我想做的事情相匹配的问题 所以我将在这里解释我的用例 如果您知道某个主题已经涵盖了该问题的答案 请随时引导我找到该主题 我有一段代码可以定期 每 20 秒 将文件上传到 Amazon S3 该文件是由另一个进程