Почему File и FileStream API не могут записывать двоичные данные без исключения, которые выдают в Ubuntu? - PullRequest
0 голосов
/ 11 февраля 2020

У меня есть приложение, которое я пытался настроить как для. NET Core 2.1, так и для NET Core 3.1 для сборки, хотя эта проблема все еще присутствует. Я реплицирую процесс потоковой передачи файлов, который происходит из нашего API (виртуальная машина Ubuntu 18.04.4) на наш сервер хранения (также виртуальную машину Ubuntu), за исключением того, что он переходит из хранилища в нашу службу OCR (третью виртуальную машину Ubuntu). На всех виртуальных машинах Ubuntu установлена ​​последняя версия. NET Core. Этот процесс потоковой передачи файлов работает по самодельному протоколу «файловой службы», который я бы предпочел не объяснять подробно, но достаточно сказать, что есть сообщение « Create », которое сообщает получателю о создании дескриптора файла. и записать первый пакет данных (пакеты размером до 64 кБ), и после получения этого сообщения « Create » инициализируется потребитель, который использует остальные данные файла как « Append *» 1006 * "сообщений из отдельной очереди до получения сообщения" Close"(не содержит полезных данных). Это, казалось, работало замечательно, пока я фактически не попытался выполнить базовые манипуляции с c над файлом, полученным на OCR (а именно, разделив страницы из документа PDF), и не понял, что файл поврежден. По догадкам я проверил размер файла на сервере OCR, и, конечно же, он точно на 64 КБ меньше исходного файла. Эта проблема никогда не возникала в части конвейера от API к хранилищу. Я проверил наш сервер хранения, чтобы убедиться, что данные не повреждены при передаче из API путем его загрузки.

По другому предположению я решил закомментировать код, который записывает Append полезные данные сообщения в файл, чтобы гарантировать, что отсутствующий пакет не был исходной Create полезной нагрузки. Я был прав, и после обработки только сообщения Create размер файла составляет 0 байт. Я проверил длину полезной нагрузки Create , а также распечатал ее как ha sh, чтобы убедиться, что это не все 0 по любой причине - никаких проблем там нет. Однако каждый раз, когда я записываю байты в фактический файл, данные не записываются и не выдается ошибка . Дескриптор файла и каталог создаются с размером 0 байтов, и все сообщения журнала печатаются. Теперь я опубликую все различные способы, которыми я пытался записать эти начальные 64 КБ данных в файл:

File API

public FileServiceResponseEnvelope Create(FileServiceMessageEnvelope message, string filePath)
        {
            var response = CreateResponse(message);
            var payload = SerializationHelper.DeserializeObject<SecureMessage>(message.SignedPayload).Payload;

            if (!File.Exists(filePath))
            {
                // 23 is enough for the first 8 bytes and the hyphens separating them.
                _logger.LogStatement($"Creating new file at [{filePath}] while appending [{payload.Length} bytes] and hash that starts with {BitConverter.ToString(payload).Substring(0, 23)}...", LogLevel.Trace, nameof(Create));
                Directory.CreateDirectory(Path.GetDirectoryName(filePath));
                File.WriteAllBytes(filePath, payload);
            }
            else
            {
                var error = $"File at [{filePath}] already exists and cannot be created.";
                response.ErrorMessage = error;
                response.Success = false;
                throw new InvalidOperationException(error);
            }

            return response;
        }

FileStream API

public FileServiceResponseEnvelope Create(FileServiceMessageEnvelope message, string filePath)
        {
            var response = CreateResponse(message);
            var payload = SerializationHelper.DeserializeObject<SecureMessage>(message.SignedPayload).Payload;

            if (!File.Exists(filePath))
            {
                // 23 is enough for the first 8 bytes and the hyphens separating them.
                _logger.LogStatement($"Creating new file at [{filePath}] while appending [{payload.Length} bytes] and hash that starts with {BitConverter.ToString(payload).Substring(0, 23)}...", LogLevel.Trace, nameof(Create));
                Directory.CreateDirectory(Path.GetDirectoryName(filePath));
                using (var stream = new FileStream(filePath, FileMode.CreateNew, FileAccess.Write, FileShare.ReadWrite))
                {
                    stream.Seek(0, SeekOrigin.Begin);
                    stream.Write(payload, 0, message.Count);
                }
            }
            else
            {
                var error = $"File at [{filePath}] already exists and cannot be created.";
                response.ErrorMessage = error;
                response.Success = false;
                throw new InvalidOperationException(error);
            }

            return response;
        }

API-интерфейс FileStream с использованием WriteByte

public FileServiceResponseEnvelope Create(FileServiceMessageEnvelope message, string filePath)
        {
            var response = CreateResponse(message);
            var payload = SerializationHelper.DeserializeObject<SecureMessage>(message.SignedPayload).Payload;

            if (!File.Exists(filePath))
            {
                // 23 is enough for the first 8 bytes and the hyphens separating them.
                _logger.LogStatement($"Creating new file at [{filePath}] while appending [{payload.Length} bytes] and hash that starts with {BitConverter.ToString(payload).Substring(0, 23)}...", LogLevel.Trace, nameof(Create));
                Directory.CreateDirectory(Path.GetDirectoryName(filePath));;
                using (var stream = new FileStream(filePath, FileMode.CreateNew, FileAccess.Write, FileShare.ReadWrite))
                {
                    stream.Seek(message.Position, SeekOrigin.Begin);
                    foreach (byte b in payload)
                    {
                        _logger.LogStatement($"Writing [{b}] to file...", LogLevel.Trace, nameof(Create));
                        stream.WriteByte(b);
                    }
                }
            }
            else
            {
                var error = $"File at [{filePath}] already exists and cannot be created.";
                response.ErrorMessage = error;
                response.Success = false;
                throw new InvalidOperationException(error);
            }

            return response;
        }

Для справки приведен обработчик сообщений Append , который работает .

AppendStream = new FileStream(DestinationPath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);

...

public FileServiceResponseEnvelope Append(FileServiceMessageEnvelope message, FileStream appendStream)
        {
            var response = CreateResponse(message);
            var payload = SerializationHelper.DeserializeObject<SecureMessage>(message.SignedPayload).Payload;

            _logger.LogStatement($"Appending [{message.Count} bytes] to [{appendStream.Name}]. Actual length: [{payload.Length} bytes]", LogLevel.Trace, nameof(Append));
            if (appendStream == null)
                throw new InvalidOperationException($"The filestream was null and therefore cannot be appended to.");

            appendStream.Write(payload, 0, message.Count);

            return response;
        }

AppendStream является членом класса потребителей и AppendStream.Dispose() вызывается при получении упомянутого ранее сообщения Close .

Любая помощь по это будет высоко ценится. Я также попытался вызвать Flush(true) и Close(), хотя я примерно на 99% уверен, что они уже вызваны для удаления в конце блока using. Я полностью запускаю dry для того, чтобы попробовать здесь, и начинаю убеждаться, что это какая-то ошибка в библиотеке System.IO. Весь приведенный выше код выполняется без ошибок, но данные не записываются в обработчик «Создать». Моя единственная оставшаяся идея заключается в том, что что-то еще берет управление над дескриптором файла, но я старался быть осторожным, чтобы писать всю эту часть кода синхронно и приводить в действие события, сигнализирующие о завершении последнего фрагмента (например, файл только обрабатывается фактической службой OCR, когда вызывается событие «Done» для потребителя, которое происходит только при получении Close ). Я пока не могу найти ничего, что могло бы взять под контроль этот дескриптор файла в моем коде. Извиняюсь за многословный пост, но я чувствовал, что должен был выложить все это, чтобы показать, насколько упряма эта проблема и что я пытался.

...