C#从网址读取csv并保存到数据库

时间:2019-01-31 12:47:23

标签: c# csv webclient

我正在尝试从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();


                        }


                    }

                }

1 个答案:

答案 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)。