【问题标题】:Return buffer while processing Stream处理流时返回缓冲区
【发布时间】:2018-02-01 13:35:30
【问题描述】:

所以我有一个文件上传表单(上传后)加密文件并将其上传到 S3 存储桶。但是,我正在做一个我想避免的额外步骤。首先,我将向您展示我现在正在做的一些代码:

using (MemoryStream memoryStream = new MemoryStream())
{
    Security.EncryptFile(FileUpload.UploadedFile.OpenReadStream(), someByteArray, memoryStream);
    memoryStream.Position = 0; // reset it's position
    await S3Helper.Upload(objectName, memoryStream);
}

我的Security.EncryptFile 方法:

public static void EncryptFile(Stream inputStream, byte[] key, Stream outputStream)
{
    CryptoStream cryptoStream;

    using (SymmetricAlgorithm cipher = Aes.Create())
    using (inputStream)
    {
        cipher.Key = key;
        // aes.IV will be automatically populated with a secure random value
        byte[] iv = cipher.IV;

        // Write a marker header so we can identify how to read this file in the future
        outputStream.WriteByte(69);
        outputStream.WriteByte(74);
        outputStream.WriteByte(66);
        outputStream.WriteByte(65);
        outputStream.WriteByte(69);
        outputStream.WriteByte(83);

        outputStream.Write(iv, 0, iv.Length);

        using (cryptoStream =
            new CryptoStream(inputStream, cipher.CreateEncryptor(), CryptoStreamMode.Read))
        {
                cryptoStream.CopyTo(outputStream);
        }
    }
}

S3Helper.Upload 方法:

public async static Task Upload(string objectName, Stream inputStream)
{
    try
    {
        // Upload a file to bucket.
        using (inputStream)
        {
            await minio.PutObjectAsync(S3BucketName, objectName, inputStream, inputStream.Length);
        }
        Console.Out.WriteLine("[Bucket] Successfully uploaded " + objectName);
    }
    catch (MinioException e)
    {
        Console.WriteLine("[Bucket] Upload exception: {0}", e.Message);
    }
}

所以,上面发生的事情是我正在创建一个 MemoryStream,运行 EncryptFile() 方法(将其输出回流),我重置流位置,最后再次重用它以将其上传到 S3 存储桶( Upload())。

问题

我想做的是以下(如果可能的话):直接将上传的文件上传到 S3 存储桶,而不是先将完整文件存储在内存中(有点像下面的代码,即使它不起作用):

await S3Helper.Upload(objectName, Security.EncryptFile(FileUpload.UploadedFile.OpenReadStream(), someByteArray));

所以我假设它必须向 Upload 方法返回一个缓冲区,该方法将上传它,并等待 EncryptFile() 方法再次返回一个缓冲区,直到文件被完全读取。任何指向正确方向的指针将不胜感激。

【问题讨论】:

  • 这根本不会产生任何收益,EncryptFile 无论如何都会在内部创建一个 MemoryStream,因为您需要将其返回到 Upload,唯一的区别是创建流的位置。
  • 您对 EncryptFile 的调用不应该为 outputStream 使用 ref 或 out 参数吗?

标签: c# asp.net .net-core


【解决方案1】:

您可以做的是创建自己的 EncryptionStream 来重载 Stream 类。当你从这个流中读取时,它会从输入流中获取一个块,对其进行加密,然后输出加密的数据。

例如,如下所示:

public class EncrypStream : Stream {

    private Stream _cryptoStream;
    private SymmetricAlgorithm _cipher;

    private Stream InputStream { get; }
    private byte[] Key { get; }

    public EncrypStream(Stream inputStream, byte[] key) {
        this.InputStream = inputStream;
        this.Key = key;
    }

    public override int Read(byte[] buffer, int offset, int count) {

        if (this._cipher == null) {
            _cipher = Aes.Create();
            _cipher.Key = Key;

            // aes.IV will be automatically populated with a secure random value
            byte[] iv = _cipher.IV;

            // Write a marker header so we can identify how to read this file in the future
            // @TODO Make sure the BUFFER is big enough...
            var idx = offset;
            buffer[idx++] = 69;
            buffer[idx++] = 74;
            buffer[idx++] = 66;
            buffer[idx++] = 65;
            buffer[idx++] = 69;
            buffer[idx++] = 83;

            Array.Copy(iv, 0, buffer, idx, iv.Length);
            offset = idx + iv.Length;

            // Startup stream
            this._cryptoStream = new CryptoStream(InputStream, _cipher.CreateEncryptor(), CryptoStreamMode.Read);
        }

        // Write block
        return this._cryptoStream.Read(buffer, offset, count);
    }

    protected override void Dispose(bool disposing) {
        base.Dispose(disposing);

        // Make SURE you properly dispose the underlying streams!
        this.InputStream?.Dispose();
        this._cipher?.Dispose();
        this._cryptoStream?.Dispose();
    }

    // Omitted other methods from stream for readability...
}

这允许您将流称为:

using (var stream = new EncrypStream(FileUpload.UploadedFile.OpenReadStream(), someByteArray)) {
     await S3Helper.Upload(objectName, stream);
}

我注意到您的上传方法需要加密数据的总字节长度,您可以查看this post here 了解如何计算此值。

(我猜 CryptoStream 没有返回加密数据的预期长度,但如果我错了,请纠正我)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-08-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-11
    • 2012-11-23
    • 1970-01-01
    相关资源
    最近更新 更多