ниже приведен код, который считывает большие двоичные объекты из моего хранилища больших двоичных объектов, а затем копирует содержимое в табличное хранилище. Теперь все работает нормально. но я знаю, что если мой файл слишком большой, чтение и копирование не удастся. Я хотел бы знать, как мы справляемся с этим в идеале, мы записываем файл временно, а не сохраняем его в памяти? Если да, может ли кто-нибудь привести мне пример или показать мне, как это сделать в моем существующем коде ниже >
public async Task<Stream> ReadStream(string containerName, string digestFileName, string fileName, string connectionString)
{
string data = string.Empty;
string fileExtension = Path.GetExtension(fileName);
var contents = await DownloadBlob(containerName, digestFileName, connectionString);
return contents;
}
public async Task<Stream> DownloadBlob(string containerName, string fileName, string connectionString)
{
Microsoft.Azure.Storage.CloudStorageAccount storageAccount = Microsoft.Azure.Storage.CloudStorageAccount.Parse(connectionString);
CloudBlobClient serviceClient = storageAccount.CreateCloudBlobClient();
CloudBlobContainer container = serviceClient.GetContainerReference(containerName);
CloudBlockBlob blob = container.GetBlockBlobReference(fileName);
if (!blob.Exists())
{
throw new Exception($"Unable to upload data in table store for document");
}
return await blob.OpenReadAsync();
}
private IEnumerable<Dictionary<string, EntityProperty>> ReadCSV(Stream source, IEnumerable<TableField> cols)
{
using (TextReader reader = new StreamReader(source, Encoding.UTF8))
{
var cache = new TypeConverterCache();
cache.AddConverter<float>(new CSVSingleConverter());
cache.AddConverter<double>(new CSVDoubleConverter());
var csv = new CsvReader(reader,
new CsvHelper.Configuration.CsvConfiguration(global::System.Globalization.CultureInfo.InvariantCulture)
{
Delimiter = ";",
HasHeaderRecord = true,
CultureInfo = global::System.Globalization.CultureInfo.InvariantCulture,
TypeConverterCache = cache
});
csv.Read();
csv.ReadHeader();
var map = (
from col in cols
from src in col.Sources()
let index = csv.GetFieldIndex(src, isTryGet: true)
where index != -1
select new { col.Name, Index = index, Type = col.DataType }).ToList();
while (csv.Read())
{
yield return map.ToDictionary(
col => col.Name,
col => EntityProperty.CreateEntityPropertyFromObject(csv.GetField(col.Type, col.Index)));
}
}
}
... с точки зрения «я должен прочитать все данные, я должен их куда-то поместить, я должен их записать ...» - вместо этого подумайте о том, как передавать данные, предоставляя «то, что хочет читать» прямое соединение с «вещью, которая хочет предоставить данные для чтения» (или, в равной степени, предоставление «вещи, на которую нужно записать» прямое соединение с «вещью, которая хочет писать», не стоя посередине и не выполняя чтение/запись во временное хранилище (память или диск)
это то, что я ищу, и чтение и запись временно были моей идеей ... но вы понимаете, о чем я здесь говорю, у вас есть какой-нибудь пример для справки. Я отредактировал код, в котором я читаю данные, переданные из ReadStream().
Теперь больше запутался. Думал, вы говорите о хранилище лазурных таблиц. Кажется, вы говорите о загрузке блок-блоба и преобразовании его в CSV, но вы опубликовали два метода чтения; Я бы ожидал чтения и записи. Концепция остается прежней; если у вас есть «что-то, что предоставляет поток, из которого вы можете читать» («что-то, что вы можете снабдить потоком, в который будет производиться запись»), и у вас есть «что-то, что предоставляет поток, в который вы можете писать» («что-то, что вы можете может предоставить поток, из которого он будет читать"), тогда вы можете соединить их и заставить их выполнять чтение/запись напрямую без временного хранилища.
Хорошо, здесь я читаю большие двоичные объекты из хранилища больших двоичных объектов Azure, большие двоичные объекты в основном представляют собой CSV-файлы, и я копирую содержимое в табличное хранилище Azure. Я не предоставил метод, при котором он вставляет данные в табличное хранилище, но это в основном берет все данные ReadCsv и вставляет их в таблицу пакетно. Я не нахожу ничего, что поставляет поток для лазурных BLOB-объектов. любая идея, если это поддерживается для больших двоичных объектов
не могли бы вы изменить мой код на то, что вы упомянули ниже, и опубликовать его как ответ. Я не могу заставить его работать с openRead
Я предполагаю, что это может выглядеть примерно так (измените свой ReadCSV, чтобы он принимал поток, а не строки):
private IEnumerable<Dictionary<string, EntityProperty>> ReadCSV(Stream source, IEnumerable<TableField> cols)
{
using (TextReader reader = new StreamReader(source))
И это (измените свой DownloadBlob, чтобы он вместо этого возвращал поток):
public async Task<Stream> GetBlobStream(string containerName, string fileName, string connectionString)
{
Microsoft.Azure.Storage.CloudStorageAccount storageAccount = Microsoft.Azure.Storage.CloudStorageAccount.Parse(connectionString);
CloudBlobClient serviceClient = storageAccount.CreateCloudBlobClient();
CloudBlobContainer container = serviceClient.GetContainerReference(containerName);
CloudBlockBlob blob = container.GetBlockBlobReference(fileName);
if (!blob.Exists())
{
throw ...
}
return await blob.OpenReadAsync();
}
А затем соедините их вместе:
var stream = GetBlobStream(...)
ReadCSV(stream, ...)
Я думал, что целью здесь было избежать чтения массивного файла в память. Как вы думаете, что data = streamReader.ReadToEnd()
собирается делать?
Как вы думаете, где живет содержимое переменной data
?
:D да.. так что идея такова: мы получаем поток в блоб, мы отдаем поток в csv-ридер, мы немного читаем из ридера, который извлекает бит из потока (надеюсь, он будет делать это, а не сделать ReadToEnd внутренне), и по мере того, как мы считываем чтение по крупицам из считывателя, мы помещаем данные в лазурное хранилище таблиц... и мы никогда не используем много памяти.
тогда как насчет кодировки utf8? Требуется ли это, если я передам поток в ReadCSV(), мне также нужно будет решить это
Вы можете указать кодировку при создании StreamReader, но в любом случае по умолчанию используется UTF8.
Обратите внимание, что UTF8 в любом случае используется по умолчанию, поэтому вам не нужно делать для него ничего особенного.
привет ... попробовал, но получил ошибку в строке csv.ReadHeader () как «Запись заголовка не найдена». в CsvHelper.CsvReader.ParseNamedIndexes() в CsvHelper.CsvReader.ReadHeader(). заголовки есть и я могу читать при чтении как MemoryStream.
Тогда вам нужно будет выполнить некоторую отладку и посмотреть, что он делает...
изменил вопрос с последним кодом, я предполагаю, что синтаксический анализ не происходит, как должно быть
Загрузите код CsvHelper и добавьте его как проект, сошлитесь на него. Таким образом, вы можете выполнить отладку в коде и посмотреть, как он себя ведет.
кажется, помощник csv не читает поток, возвращаемый из openreadasync(). любой альтернативный подход с файловым потоком?
Это не имеет смысла; он будет читать из ридера, который читает из потока памяти, но не читает из ридера, который читает из сетевого потока? CsvHelper не знает о потоке, он знает только о читателе
это странно, но это то, что происходит. я разместил это как отдельный вопрос, и люди предполагают, что это ограничение csvhelper здесь, что он не читает сетевой поток..stackoverflow.com/questions/65442508/…
Вы прочитали это из блоба. не могли бы вы вставить свой код, как вы пытались
Да, я прочитал это с блоба. Я просто пишу ответ с точным кодом
По вашему настоянию, что CsvHelper не может читать из потока, подключенного к большому двоичному объекту, я кое-что собрал:
CSV с моего диска:
В моем хранилище больших двоичных объектов:
В моем отладчике есть запись CAf255 OK по Read/GetRecord:
Или по EnumerateRecords:
Используя этот код:
private async void button1_Click(object sender, EventArgs e)
{
var cstr = "MY CONNECTION STRING HERE";
var bbc = new BlockBlobClient(cstr, "temp", "call.csv");
var s = await bbc.OpenReadAsync(new BlobOpenReadOptions(true) { BufferSize = 16384 });
var sr = new StreamReader(s);
var csv = new CsvHelper.CsvReader(sr, new CsvConfiguration(CultureInfo.CurrentCulture) { HasHeaderRecord = true });
var x = new X();
//try by read/getrecord (breakpoint and skip over it if you want to try the other way)
while(await csv.ReadAsync())
{
var rec = csv.GetRecord<X>();
Console.WriteLine(rec.Sid);
}
//try by await foreach
await foreach (var r in csv.EnumerateRecordsAsync(x))
{
Console.WriteLine(r.Sid);
}
}
О, и класс, который представляет запись CSV в моем приложении (я смоделировал только одно свойство, Sid, чтобы доказать концепцию):
class X {
public string Sid{ get; set; }
}
Может быть, немного вернемся назад, начнем с простого. Одна строковая поддержка в вашем CSV, без уступок и т. д., Просто получите чтение файла в порядке. Я также не беспокоился обо всех заголовочных ошибках - кажется, просто работает нормально, говоря «файл имеет заголовки» в параметрах - вы можете видеть, что у моего отладчика есть экземпляр X с правильно заполненным свойством Sid, показывающим первое значение. Я выполнил еще несколько циклов, и они тоже заполнились нормально.
спасибо, я пробую все упомянутое здесь. большое спасибо
Я сделал так, как вы, и хотя из вашего метода выше не возникает ошибки, он не попадает в код внутри цикла while и цикла foreach. что-то с асинхронной проблемой?? Также что нового в BlobOpenReadOptions(true) {BufferSize = 16384}. я не могу это решить
Как вы можете видеть, на снимке экрана CSV на моем компьютере, на снимке экрана моего Azure Storage Explorer, показывающем, что он загружен в хранилище BLOB-объектов, и на снимке экрана моего отладчика Visual Studio, который загрузил файл, проанализировал его в CSV и вошел в цикл где он показывает проанализированный контент, я не совсем уверен, что я могу сделать, чтобы убедить вас, что код работает - возможно, сделайте запись экрана, на которой я выполняю один шаг в отладчике?
Я выяснил проблему с BlobOptionRead, я не имел в виду правильную версию, но проблема остается, код не входит в цикл foreach или while
Тогда записей нет?
файл есть, поток sr тоже имеет длину и позицию
Console.WriteLIne(sr.ReadToEnd())
должен распечатать весь файл, поэтому отладьте, почему ваш читатель Csv не читает его. Мой CsvReader читает мой файл и даже читает ваш файл (когда я скопировал и сохранил ваши данные с помощью блокнота). Используйте другой файл CSV, проверьте окончания строк и т. д.
наконец понял, что это была моя ошибка, когда StreamReader читал поток и устанавливал позицию последней в потоке вместо нуля, и это было причиной того, что предыдущий метод не работал. удалил стримридер и теперь все работает нормально. Спасибо за вашу помощь. ребята, как вы, причина того, что stackoverflow настолько успешен.
Вы (ваш код) вообще не читаете из средства чтения потока и не устанавливаете позицию потока напрямую; вы просто получаете поток, оборачиваете его потоковым считывателем и отдаете потоковый читатель csvreader — он выполняет все чтение и позиционирование. Если вы прочитаете поток до конца, а затем отдадите его для чтения чему-то другому, «чему-то еще» не останется ничего, что можно было бы прочитать.
Где тот бит, что "скопировать содержимое в табличное хранилище"? Кроме того, подумайте о потоках — они похожи на каналы, у вас есть что-то, что вы хотите прочитать, и что-то, на что вы хотите записать. Прямо сейчас вы используете методы, в которых вы даете блок-блобу поток памяти, и он записывает в него, затем вы (вероятно) сбрасываете его и передаете его табличному блобу и заставляете его читать из него.. но почему бы просто не запросить у блок-блоба его поток и передать этот поток в табличный BLOB-объект и заставить его читать из него? Это почти вся идея потока; это просто поток данных. Ты все еще думаешь обо всем этом...