处理Stream时返回缓冲区

时间:2018-02-01 13:35:30

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

所以我有一个文件上传表单(上传后)加密文件并将其上传到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()方法再次返回缓冲区,直到文件被完全读取。任何指向正确方向的指针都将非常感激。

1 个答案:

答案 0 :(得分:2)

您可以做的是创建自己的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没有返回加密数据的预期长度,但如果我错了,请纠正我)