Файловый API, кажется, всегда записывает поврежденные файлы при использовании в цикле, за исключением последнего файла - PullRequest
0 голосов
/ 28 июня 2019

Я знаю, что название длинное, но оно точно описывает проблему.Я не знал, как еще это объяснить, потому что это полностью там.

У меня есть утилита, написанная на C # для .NET Core 2.1, которая загружает и дешифрует файлы (шифрование AES), изначально загруженные нашими клиентами изнаше зашифрованное хранилище, поэтому они могут быть повторно обработаны через некоторые из наших служб в случае их отказа.Эта утилита запускается через CLI, используя идентификаторы базы данных для файлов в качестве аргументов, например, download.bat 101 102 103 загрузит 3 файла с соответствующими идентификаторами.Я получаю байтовые данные через очередь сообщений (на самом деле не намного больше, чем сокет TCP), которая описывает изображение .TIF.

У меня есть веская причина полагать, что байтовые данные никогда не повреждаются насервер.Причина в том, что когда я запускаю утилиту только с одним параметром ID, таким как download.bat 101, она работает просто отлично.Кроме того, когда я запускаю его с несколькими идентификаторами, последний загруженный утилитой файл всегда остается без изменений, а остальные всегда повреждены.

Это странное поведение сохраняется в двух разных реализациях для записи байтовых данных в файл.Эти реализации приведены ниже.

File.ReadAllBytes реализация:

private static void WriteMessageContents(FileServiceResponseEnvelope envelope, string destination, byte[] encryptionKey, byte[] macInitialVector)
        {
            using (var inputStream = new MemoryStream(envelope.Payload))
            using (var outputStream = new MemoryStream(envelope.Payload.Length))
            {
                var sha512 = YellowAesEncryptor.DecryptStream(inputStream, outputStream, encryptionKey, macInitialVector, 0);
                File.WriteAllBytes(destination, outputStream.ToArray());

                _logger.LogStatement($"Finished writing [{envelope.Payload.Length} bytes] to [{destination}].", LogLevel.Debug);
            }
        }

FileStream реализация:

private static void WriteMessageContents(FileServiceResponseEnvelope envelope, string destination, byte[] encryptionKey, byte[] macInitialVector)
        {
            using (var inputStream = new MemoryStream(envelope.Payload))
            using (var outputStream = new MemoryStream(envelope.Payload.Length))
            {
                var sha512 = YellowAesEncryptor.DecryptStream(inputStream, outputStream, encryptionKey, macInitialVector, 0);
                using (FileStream fs = new FileStream(destination, FileMode.Create))
                {
                    var bytes = outputStream.ToArray();
                    fs.Write(bytes, 0, envelope.Payload.Length);
                    _logger.LogStatement($"File byte content: [{string.Join(", ", bytes.Take(16))}]", LogLevel.Trace);
                    fs.Flush();
                }

                _logger.LogStatement($"Finished writing [{envelope.Payload.Length} bytes] to [{destination}].", LogLevel.Debug);
            }
        }

Этот метод вызывается из цикла for, который сначала получаетсообщения, которые я описал ранее, а затем передают свои полезные данные вышеуказанному методу:

using (var requestSocket = new RequestSocket(fileServiceEndpoint))
            {
                // Envelopes is constructed beforehand
                foreach (var envelope in envelopes)
                {
                    var timer = Stopwatch.StartNew();
                    requestSocket.SendMoreFrame(messageTypeBytes);
                    requestSocket.SendMoreFrame(SerializationHelper.SerializeObjectToBuffer(envelope));
                    if (!requestSocket.TrySendFrame(_timeout, signedPayloadBytes, signedPayloadBytes.Length))
                    {
                        var message = $"Timeout exceeded while processing [{envelope.ActionType}] request.";
                        _logger.LogStatement(message, LogLevel.Error);
                        throw new Exception(message);
                    }

                    var responseReceived = requestSocket.TryReceiveFrameBytes(_timeout, out byte[] responseBytes);

                    ...

                    var responseEnvelope = SerializationHelper.DeserializeObject<FileServiceResponseEnvelope>(responseBytes);

                    ...

                    _logger.LogStatement($"Received response with payload of [{responseEnvelope.Payload.Length} bytes].", LogLevel.Info);
                    var destDir = downloadDetails.GetDestinationPath(responseEnvelope.FileId);
                    if (!Directory.Exists(destDir))
                        Directory.CreateDirectory(destDir);

                    var dest = Path.Combine(destDir, idsToFileNames[responseEnvelope.FileId]);

                    WriteMessageContents(responseEnvelope, dest, encryptionKey, macInitialVector);
                }
            }

Я также знаю, что TIF-файлы имеют очень специфический заголовок, который выглядит примерно так в необработанных байтах:

[73, 73, 42, 0, 8, 0, 0, 0, 20, 0...

Он всегда начинается с «II» (73, 73) или «ММ» (77, 77), за которым следует 42 (вероятно, ссылка автостопщика).Я проанализировал байты, написанные утилитой.Последний файл всегда имеет заголовок, который похож на этот.Остальные всегда случайные байты;казалось бы перемешанные или неправильно упорядоченные двоичные данные изображения.Любое понимание этого было бы очень ценно, потому что я не могу сосредоточиться на том, что мне даже нужно сделать, чтобы диагностировать это.

ОБНОВЛЕНИЕ

Я смогчтобы выяснить эту проблему с помощью elgonzo в комментариях.Иногда помогает не прямой ответ, а кто-то ковыряет в голове, пока вы не посмотрите в нужном месте.

1 Ответ

0 голосов
/ 28 июня 2019

Хорошо, поскольку я подозревал, что это глупая ошибка (у меня были серьезные сомнения в том, что File API просто так недоработан). Мне просто нужна помощь, чтобы обдумать это. Был дополнительный фрагмент кода, который я не опубликовал, который кусал меня, когда я получал метаданные для файла, чтобы я мог затем запросить файл из нашего хранилища.

byte[] encryptionKey = null;
byte[] macInitialVector = null;
...
using (var conn = new SqlConnection(ConnectionString))
            using (var cmd = new SqlCommand(uploadedFileQuery, conn))
            {
                conn.Open();

                var reader = cmd.ExecuteReader();
                while (reader.Read())
                {
                    FileServiceMessageEnvelope readAllEnvelope = null;
                    var originalFileName = reader["UploadedFileClientName"].ToString();
                    var fileId = Convert.ToInt64(reader["UploadedFileId"].ToString());

                    //var originalFileExtension = originalFileName.Substring(originalFileName.IndexOf('.'));
                    //_logger.LogStatement($"Scooped extension: {originalFileExtension}", LogLevel.Trace);
                    envelopes.Add(readAllEnvelope = new FileServiceMessageEnvelope
                    {
                        ActionType = FileServiceActionTypeEnum.ReadAll,
                        FileType = FileTypeEnum.UploadedFile,
                        FileName = reader["UploadedFileServerName"].ToString(),
                        FileId = fileId,
                        WorkerAuthorization = null,
                        BinaryTimestamp = DateTime.Now.ToBinary(),
                        Position = 0,
                        Count = Convert.ToInt32(reader["UploadedFileSize"]),
                        SignerFqdn = _messengerConfig.FullyQualifiedDomainName
                    });

                    readAllEnvelope.SignMessage(_messengerConfig.PrivateKeyBytes, _messengerConfig.PrivateKeyPassword);
                    signedPayload = new SecureMessage { Payload = new byte[0] };
                    signedPayload.SignMessage(_messengerConfig.PrivateKeyBytes, _messengerConfig.PrivateKeyPassword);
                    signedPayloadBytes = SerializationHelper.SerializeObjectToBuffer(signedPayload);
                    encryptionKey = (byte[])reader["UploadedFileEncryptionKey"];
                    macInitialVector = (byte[])reader["UploadedFileEncryptionMacInitialVector"];
                }

                conn.Close();
            }

Наблюдатели с орлиными глазами могут понять, что я не правильно связал encryptionKey и macInitialVector с правильной записью, поскольку каждый файл имеет уникальный ключ и вектор. Это означает, что я использовал ключ для одного файлов для расшифровки всех их , поэтому все они были повреждены, за исключением одного файла - они не были должным образом расшифрованы. Я решил эту проблему, соединив их вместе с идентификатором в простом POCO и получив соответствующий ключ и вектор для каждого файла при расшифровке.

...