У меня есть приложение, которое я пытался настроить как для. 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 ). Я пока не могу найти ничего, что могло бы взять под контроль этот дескриптор файла в моем коде. Извиняюсь за многословный пост, но я чувствовал, что должен был выложить все это, чтобы показать, насколько упряма эта проблема и что я пытался.