【问题标题】:C# Read csv from url and save to databaseC#从url读取csv并保存到数据库
【发布时间】:2019-01-31 12:47:22
【问题描述】:

我正在尝试从 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 服务使用分页并返回 100 行(由 &num 参数确定,最大值为 100)。在第一个请求之后,我可以使用 &next 参数根据 ID 获取接下来的 100 行。例如网址

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


    }

但我不确定这是否是一种“最新”的方式,或者是否有更好的方式(更容易/更简单)?换句话说,我不知道使用 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();


                        }


                    }

                }

【问题讨论】:

    标签: c# csv webclient


    【解决方案1】:

    System.Web.Http 上的 HttpClient 类从 .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();
    

    这里的新任务是接受一个“动作”,或者换句话说,一个返回 void 的函数作为参数。然后你开始任务。请注意,它是异步的,因此您在 t.Start() 之后的任何代码都可能在您的任务完成之前运行良好。

    关于您关于流是否读取“内存中”的问题,在代码中的“流”上运行 GetType() 会导致“MemoryStream”类型,尽管它似乎只被识别为“流” " 编译时的对象。 MemoryStream 肯定是在内存中的。我不确定是否有任何其他类型的流对象在幕后保存临时文件,但我倾向于不这样做。

    但是,尽管值得称赞,但通常不需要研究班级的内部运作,因为您担心处置。对于任何类,只要看看它是否实现了 IDisposable。如果是这样,那么就像您在代码中所做的那样,放入“使用”语句。当程序按预期或通过错误终止时,程序将在控制权超出“使用”块后执行适当的处​​理。

    HttpClient 实际上是较新的方法。据我了解,它并没有取代 WebClient 的所有功能,但在许多方面都更强大。请参阅thisSO 网站了解比较这两个类的更多详细信息。

    另外,关于 WebClient 的一些知识是它可以很简单,但也有局限性。如果遇到问题,您将需要查看 HttpWebRequest 类,这是一个“较低级别”的类,可让您更好地访问事物的具体细节(例如使用 cookie)。

    【讨论】:

    • 您好,感谢您详细的回答,并提供了很多很好的信息。您会如何看待使用 TextFieldParser parser = new TextFieldParser(myStream) (我编辑了我的问题的结尾),因为它擅长处理 csv 数据,我认为这可能会很好用?你是否也认为使用这个 this 是在记忆中?
    • 我不能绝对自信地回答,但我无法想象任何体面的实现都需要在后台创建临时文件。至于仅拆分字符串是否更高效,我会说如果这就是您需要做的全部,也许不是。但测试是回答这个问题的方法。但是如果你需要更多的灵活性,TextFieldParser 有更多的功能。此外,如果您的任务很常见,请查找 CsvHelper 或类似库。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-02
    相关资源
    最近更新 更多