C# 从 url 读取 csv 并保存到数据库
C# Read csv from url and save to database
我正在尝试从 Web 服务的 csv 文件中获取数据。
如果我将 url 粘贴到我的浏览器中,将下载 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 服务使用分页和 returns 100 行(由最大为 100 的 &num 参数决定)。在第一个请求之后,我可以使用 &next 参数根据 ID 获取接下来的 100 行。例如 url
http://testWebservice123.com/Example.csv?auth=abc&number=100&next=100
将使我从 ID 101 到 200 行。因此,如果有很多行,我最终会下载大量 csv 文件并将它们保存到硬盘驱动器。因此,我不想先下载 csv 文件并将它们保存到硬盘,而是想直接从 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
}
但我不确定这是 "up to date" 的方法,还是有更好的方法 (easier/simpler)?换句话说,我不确定使用 WebClient 和 StreamReader 是否是正确的方法?
在此线程中:how to read a csv file from a url?
提到 WebClient.DownloadString 以及 WebRequest。但是,如果我想在不将 csv 保存到硬盘的情况下写入数据库,哪个是最佳选择?
Furhtermore - 我采用的方法是将数据保存到幕后的临时磁盘存储还是将所有数据读入内存然后在循环完成时处理?
我已阅读以下文档,但似乎无法找出它在幕后的作用:
流阅读器: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();
}
}
}
System.Web.Http 上的 HttpClient class 从 .Net 4.5 开始可用。您必须使用异步代码,但如果您正在处理网络,那么进入它并不是一个坏主意。
作为示例数据,我将使用 jsonplaceholder's "todo" 列表。它提供 json 数据,而不是 csv 数据,但它提供了一个足够简单的结构,可以在下面的示例中满足我们的目的。
这是核心功能,它以与您的 "testWebService123" 站点类似的方式从 jsonplaceholder 获取,尽管我只是获取前 3 个待办事项,而不是测试何时我已经到达最后一页(您可能会保留您的 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();
新任务,这里是接受一个"action",或者换句话说,一个returns无效的函数,作为参数。然后你开始任务。请注意,它是异步的,因此您在 t.Start() 之后的任何代码都可能在您的任务完成之前 运行。
关于您关于流是否读取 "in memory" 的问题,代码中 运行ning GetType() on "stream" 导致 "MemoryStream" 类型,尽管它似乎只在编译时被识别为 "Stream" 对象。 MemoryStream 肯定在内存中。我不太确定是否有任何其他类型的流对象在幕后保存临时文件,但我倾向于不这样做。
但是查看 class 的内部工作机制虽然值得称赞,但通常不需要您担心处置问题。对于任何 class,只需查看它是否实现了 IDisposable。如果是,则按照您在代码中所做的那样,放入 "using" 语句。当程序终止时,如预期或由于错误,程序将在控制权从 "using" 块中移出后实施适当的处置。
HttpClient 实际上是较新的方法。据我了解,它并没有取代 WebClient 的所有功能,但在许多方面更强大。请参阅 this SO 站点以了解比较这两个 classes 的更多详细信息。
此外,关于 WebClient 的一些知识是它可以很简单,但有限制。如果您 运行 研究问题,您将需要查看 HttpWebRequest class,它是一个 "lower level" class 可以让您更好地了解事物的具体细节(例如使用 cookie)。
我正在尝试从 Web 服务的 csv 文件中获取数据。 如果我将 url 粘贴到我的浏览器中,将下载 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 服务使用分页和 returns 100 行(由最大为 100 的 &num 参数决定)。在第一个请求之后,我可以使用 &next 参数根据 ID 获取接下来的 100 行。例如 url
http://testWebservice123.com/Example.csv?auth=abc&number=100&next=100
将使我从 ID 101 到 200 行。因此,如果有很多行,我最终会下载大量 csv 文件并将它们保存到硬盘驱动器。因此,我不想先下载 csv 文件并将它们保存到硬盘,而是想直接从 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
}
但我不确定这是 "up to date" 的方法,还是有更好的方法 (easier/simpler)?换句话说,我不确定使用 WebClient 和 StreamReader 是否是正确的方法?
在此线程中:how to read a csv file from a url?
提到WebClient.DownloadString 以及 WebRequest。但是,如果我想在不将 csv 保存到硬盘的情况下写入数据库,哪个是最佳选择?
Furhtermore - 我采用的方法是将数据保存到幕后的临时磁盘存储还是将所有数据读入内存然后在循环完成时处理? 我已阅读以下文档,但似乎无法找出它在幕后的作用: 流阅读器: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();
}
}
}
System.Web.Http 上的 HttpClient class 从 .Net 4.5 开始可用。您必须使用异步代码,但如果您正在处理网络,那么进入它并不是一个坏主意。
作为示例数据,我将使用 jsonplaceholder's "todo" 列表。它提供 json 数据,而不是 csv 数据,但它提供了一个足够简单的结构,可以在下面的示例中满足我们的目的。
这是核心功能,它以与您的 "testWebService123" 站点类似的方式从 jsonplaceholder 获取,尽管我只是获取前 3 个待办事项,而不是测试何时我已经到达最后一页(您可能会保留您的 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();
新任务,这里是接受一个"action",或者换句话说,一个returns无效的函数,作为参数。然后你开始任务。请注意,它是异步的,因此您在 t.Start() 之后的任何代码都可能在您的任务完成之前 运行。
关于您关于流是否读取 "in memory" 的问题,代码中 运行ning GetType() on "stream" 导致 "MemoryStream" 类型,尽管它似乎只在编译时被识别为 "Stream" 对象。 MemoryStream 肯定在内存中。我不太确定是否有任何其他类型的流对象在幕后保存临时文件,但我倾向于不这样做。
但是查看 class 的内部工作机制虽然值得称赞,但通常不需要您担心处置问题。对于任何 class,只需查看它是否实现了 IDisposable。如果是,则按照您在代码中所做的那样,放入 "using" 语句。当程序终止时,如预期或由于错误,程序将在控制权从 "using" 块中移出后实施适当的处置。
HttpClient 实际上是较新的方法。据我了解,它并没有取代 WebClient 的所有功能,但在许多方面更强大。请参阅 this SO 站点以了解比较这两个 classes 的更多详细信息。
此外,关于 WebClient 的一些知识是它可以很简单,但有限制。如果您 运行 研究问题,您将需要查看 HttpWebRequest class,它是一个 "lower level" class 可以让您更好地了解事物的具体细节(例如使用 cookie)。