我正在尝试从Web服务的csv文件中获取数据。 如果将网址粘贴到浏览器中,则将下载csv,其外观类似于以下示例:
"ID","ProductName","Company"
"1","Apples","Alfreds futterkiste"
"2","Oranges","Alfreds futterkiste"
"3","Bananas","Alfreds futterkiste"
"4","Salad","Alfreds futterkiste"
...next 96 rows
但是,我不想先下载csv文件,然后再从中提取数据。 Web服务使用分页并返回100行(由&num参数确定,最大值为100)。在第一个请求之后,我可以使用&next-parameter根据ID提取接下来的100行。例如网址
http://testWebservice123.com/Example.csv?auth=abc&number=100&next=100
会让我的行从ID 101到200。所以,如果有很多行,我最终将下载大量的csv文件并将其保存到硬盘中。因此,与其先下载csv文件并将其保存到HDD,不如我想直接从Web服务获取数据,以便能够直接写入数据库而无需保存csv文件。
经过一番搜索,我提出了以下解决方案
static void Main(string[] args)
{
string startUrl = "http://testWebservice123.com/Example.csv?auth=abc&number=100";
string url = "";
string deltaRequestParameter = "";
string lastLine;
int numberOfLines = 0;
do
{
url = startUrl + deltaRequestParameter;
WebClient myWebClient = new WebClient();
using (Stream myStream = myWebClient.OpenRead(url))
{
using (StreamReader sr = new StreamReader(myStream))
{
numberOfLines = 0;
while (!sr.EndOfStream)
{
var row = sr.ReadLine();
var values = row.Split(',');
//do whatever with the rows by now - i.e. write to console
Console.WriteLine(values[0] + " " + values[1]);
lastLine = values[0].Replace("\"", ""); //last line in the loop - get the last ID.
numberOfLines++;
deltaRequestParameter = "&next=" + lastLine;
}
}
}
} while (numberOfLines == 101); //since the header is returned each time the number of rows will be 101 until we get to the last request
}
但是我不确定这是否是“最新”的方法,或者是否有更好的方法(更简单/更简单)?换句话说,我不确定使用WebClient和StreamReader是否是正确的方法?
在此线程中:how to read a csv file from a url?
提到了WebClient.DownloadString以及WebRequest。但是,如果我想在不将csv保存到hdd的情况下写入数据库,那是最好的选择?
Furhtermore-我采用的方法是将数据保存到幕后的临时磁盘存储中,还是将所有数据读取到内存中,然后在循环完成后处理? 我已经阅读了以下文档,但似乎无法了解其幕后工作: StreamReader:https://docs.microsoft.com/en-us/dotnet/api/system.io.streamreader?view=netframework-4.7.2
流:https://docs.microsoft.com/en-us/dotnet/api/system.io.stream?view=netframework-4.7.2
编辑: 我想我也可以使用以下“ TextFieldParser” ...但我的问题确实仍然相同:
(使用Microsoft.VisualBasic程序集)
using (Stream myStream = myWebClient.OpenRead(url))
{
using (TextFieldParser parser = new TextFieldParser(myStream))
{
numberOfLines = 0;
parser.TrimWhiteSpace = true; // if you want
parser.Delimiters = new[] { "," };
parser.HasFieldsEnclosedInQuotes = true;
while (!parser.EndOfData)
{
string[] line = parser.ReadFields();
Console.WriteLine(line[0].ToString() + " " + line[1].ToString());
numberOfLines++;
deltaRequestParameter = "&next=" + line[0].ToString();
}
}
}
答案 0 :(得分:1)
System.Web.Http上的HttpClient类从.Net 4.5开始可用。您必须使用异步代码,但是如果您要处理Web,那么使用它不是一个坏主意。
作为示例数据,我将使用jsonplaceholder's“待办事项”列表。它提供了json数据,而不是csv数据,但是它提供了足够简单的结构,可以在下面的示例中满足我们的目的。
这是核心功能,它以与您的“ testWebService123”网站类似的方式从jsonplaceholder获取,尽管我只是获得前三个待办事项,而不是测试我何时到达最后一页(您可能会使您的do-while逻辑保持不变。
async void DownloadPagesAsync() {
for (var i = 1; i < 3; i++) {
var pageToGet = $"https://jsonplaceholder.typicode.com/todos/{i}";
using (var client = new HttpClient())
using (HttpResponseMessage response = await client.GetAsync(pageToGet))
using (HttpContent content = response.Content)
using (var stream = (MemoryStream) await content.ReadAsStreamAsync())
using (var sr = new StreamReader(stream))
while (!sr.EndOfStream) {
var row =
sr.ReadLine()
.Replace(@"""", "")
.Replace(",", "");
if (row.IndexOf(":") == -1)
continue;
var values = row.Split(':');
Console.WriteLine($"{values[0]}, {values[1]}");
}
}
}
这就是调用函数的方式,就像在Main()方法中那样:
Task t = new Task(DownloadPagesAsync);
t.Start();
新任务在这里接受一个“动作”,或者换句话说,一个返回void的函数作为参数。然后,您开始任务。请注意,它是异步的,因此t.Start()之后的任何代码都可以在任务完成之前很好地运行。
关于您的问题,即流是否读取“内存中”,在代码中的“ stream”上运行GetType()会导致“ MemoryStream”类型,尽管它似乎只能被识别为“ Stream” “在编译时对象。 MemoryStream肯定在内存中。我不确定所有其他流对象是否在后台保存了临时文件,但我倾向于不这样做。
但是,尽管值得称赞,但调查班级的内部运作方式通常并不因您担心处置而感到焦虑。对于任何类,只需查看其是否实现IDisposable。如果是这样,则像在代码中所做的那样,放入“ using”语句。当程序终止时(如预期的那样或由于错误而终止),在控制权移出“使用”块之后,程序将实施适当的处置。
HttpClient实际上是较新的方法。据我了解,它不能替代WebClient的所有功能,但在许多方面都更强大。有关比较这两个类的更多详细信息,请参见this SO网站。
此外,关于WebClient的一些知识是它可以很简单,但有一定局限性。如果遇到问题,则需要查看HttpWebRequest类,它是一个“较低级别”的类,它使您可以更轻松地访问事物的细节(例如使用cookie)。