百度地圖雲麻點之批量上傳、實時顯示數據篇

      上篇博文你可能用到的百度地圖效果(付源碼)介紹了幾個比較實用的百度地圖特效,其中重點介紹了海量數據上傳及響應的問題,前端展現能夠經過LBS雲麻點來展現,經過這個能夠解決批量數據Marker響應特慢的性能問題。首先在百度雲服務器上建完表以後,咱們能夠經過後臺的管理平臺直接把數據傳上去,做爲咱們的初始數據。這部分數據有了以後,接下來要作的就是想辦法手動同步數據,更智能一點就是實時同步數據,接下來就帶你一步步實現這個過程。html

      此次在正文開始以前,我想先作一次吐槽君。最近壓力有點兒大,先來發一下牢騷。三人行,則必有我師。不管你職位的高低,工做沒有貴賤,當你是新手的時候,你應該要放低姿態,當你是老手的時候,更要放低姿態,由於成熟的麥子都是低着頭的。。。上帝爲你開了一扇門,就確定會爲你關一扇窗,不管何時,都不要以爲你比別人優越多少。當工做中你發現別人犯了錯時,你沒有義務去糾正別人,幫助別人改正,可是退一萬步,你至少不該該冷嘲熱諷吧。本是同根生,相煎何太急?程序員又何須爲難程序員呢?你可能比別人多了兩年的經驗,可能比別人多寫了些代碼,但是你能保證各方面都比別人優秀嗎?若是你是老手,多幫幫別人,你漫不經心的一句話可能讓別人脫離了迷惘;若是你是新手,努力學習,不要永遠作新手!最後,能在一塊兒工做都是緣分,且行且珍惜!前端

      首先,說說此次同步過程當中發現的幾個問題及解決方案。一、百度雲麻點樣式沒法自定義,只能使用官方的一些座標點的樣式;二、雖然官方給出的數據是一天請求10萬次,可是若是請求頻繁的話,會返回錯誤的數據;三、百度雲檢索最多隻能返回2000條數據,若是須要返回更多數據,能夠經過雲存儲裏的查詢指定條件的數據列表接口來實現;四、雲服務器數據會有緩存,有時候更新不及時;五、上傳數據的csv格式裏有一列是空的,不知道是幹什麼用的。      程序員

      接下來開始今天的正文,數據同步這塊兒,這裏經歷了文章開頭說到的兩個過程,由手動同步升級成自動同步。剛開始作的是傻瓜似的,在頁面上放了一個按鈕,在按鈕點擊的時候向handler發送請求,而後去獲取本地數據,而後更新到百度雲服務器。下圖是上傳數據須要的接口:數據庫

     經過API接口能夠看到,發送的請求參數也比較少,三個必須參數,一個表ID geotable_id,一個csv文件,即須要上傳的數據,另一個是訪問權限ak。到這裏咱們大體的思路就出來了,首先生成csv格式的數據文件,而後獲取配置的ak和geotable_id,而後封裝參數,接着發送Post請求。下面咱們來一步一步實現這個過程:json

      首先生成csv格式的數據文件。這裏發生的場景是這樣的:當咱們最初在百度雲服務器建表的時候,表建完以後咱們會把最初的數據經過API控制檯的數據管理後臺把數據批量上傳上去,而後本地數據會不斷更新,當咱們點擊頁面同步數據按鈕的時候,最重要的一個判斷就是——須要同步哪些數據。這裏能夠有兩種方案,第一,經過配置表的參數來存本地更新以前最大的一條記錄的主鍵,而後同步的時候去找比這條數據主鍵更大的數據;第二,直接向百度服務器獲取數據,由於百度雲存儲那邊能夠對查詢出來的數據進行倒敘排序,因此咱們只須要按排序字段進行倒敘排列,而後取top1條記錄,這樣也能夠獲得最大記錄的主鍵。獲得這個主鍵以後,接下來作的操做就簡單了,每次點擊同步的時候,對比服務器上存的最大記錄主鍵跟本地數據庫當前最大記錄主鍵的大小,若是服務器上的數據比本地小,則有數據須要同步,反之則不。關鍵代碼:windows

      如下是同步數據的關鍵代碼:api

        #region 同步數據
        /// <summary>
        /// 同步數據 /// </summary>
        /// <param name="context"></param>
        private void HttpToSyncBaiduData(HttpContext context) { string posturl = "http://api.map.baidu.com/geodata/v3/poi/upload"; Users userBll = new Users(); int userId = Globals.SafeInt(context.Request.Form["userId"], -1); if (userId > 0)//當前雲存儲中最大的UserId
 { DataSet ds = userBll.GetUserData(userId); if (!DataSetTools.DataSetIsNull(ds)) { string originalName = ExportDataGridToCSV(ds.Tables[0]); NameValueCollection nvc = new NameValueCollection(); nvc.Add("geotable_id", geotable_id.ToString()); nvc.Add("ak", ak); string contents = HttpUploadFile(posturl, originalName, "poi_list", "application/vnd.ms-excel", nvc); FileManage.DeleteFile(originalName); //刪除原文件
                    JsonObject jsonObject = JsonConvert.Import<JsonObject>(contents); context.Response.Write(jsonObject); } else { JsonObject jsonObject = new JsonObject(); jsonObject.Put("status", "-1"); context.Response.Write(jsonObject); } } } #endregion
View Code

     這裏涉及了兩個方法,一個是將DataTable轉成CSV文件,一個是發送Post請求,如下是代碼:  緩存

        #region DataGrid轉CSV文件
        /// <summary>
        /// Export the data from datatable to CSV file /// </summary>
        /// <param name="grid">DataGrid</param>
        public string ExportDataGridToCSV(DataTable dt) { string path = null; string strFile = "syncUserData" + DateTime.Now.ToString("yyyyMMddhhmmss") + ".csv"; path = HttpContext.Current.Server.MapPath(strFile); using (System.IO.FileStream fs = new FileStream(path, System.IO.FileMode.Create, System.IO.FileAccess.Write)) { using (StreamWriter sw = new StreamWriter(fs, new System.Text.UTF8Encoding())) { for (int i = 0; i < dt.Columns.Count - 1; i++) { sw.Write(dt.Columns[i].ColumnName); sw.Write(","); } sw.Write(dt.Columns[dt.Columns.Count - 1].ColumnName); sw.WriteLine(""); for (int i = 0; i < dt.Rows.Count; i++) { for (int j = 0; j < dt.Columns.Count - 1; j++) { sw.Write(DelQuota(dt.Rows[i][j].ToString())); sw.Write(","); } sw.Write(DelQuota(dt.Rows[i][dt.Columns.Count - 1].ToString())); sw.WriteLine(""); } sw.Flush(); } } return path; } #endregion

        #region 過濾特殊字符
        /// <summary>
        /// Delete special symbol /// </summary>
        /// <param name="str">須要過濾的字符串</param>
        /// <returns></returns>
        public string DelQuota(string str) { string result = str; string[] strQuota = { "~", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "`", ";", "'", ",", "/", ":", "/,", "<", ">", "?", "|" }; foreach (string item in strQuota) { if (result.Contains(item)) { result = result.Replace(item, ""); } } return result; } #endregion
View Code

     如下是直接發送Post請求的代碼,以前在網上找了不少C#發送Post請求的,用的時候都是各類問題,後來才找到下邊這個,確實是能夠用:服務器

        #region Post方式提交請求
        /// <summary>
        /// Post方式提交請求 /// </summary>
        /// <param name="url">請求地址</param>
        /// <param name="file">文件</param>
        /// <param name="paramName">參數名稱</param>
        /// <param name="contentType">類型</param>
        /// <param name="nvc">參數集合</param>
        /// <returns></returns>
        public string HttpUploadFile(string url, string file, string paramName, string contentType, NameValueCollection nvc) { string result = string.Empty; string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x"); byte[] boundarybytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n"); HttpWebRequest wr = (HttpWebRequest)WebRequest.Create(url); wr.ContentType = "multipart/form-data; boundary=" + boundary; wr.Method = "POST"; wr.KeepAlive = true; wr.Credentials = System.Net.CredentialCache.DefaultCredentials; Stream rs = wr.GetRequestStream(); string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}"; foreach (string key in nvc.Keys) { rs.Write(boundarybytes, 0, boundarybytes.Length); string formitem = string.Format(formdataTemplate, key, nvc[key]); byte[] formitembytes = System.Text.Encoding.UTF8.GetBytes(formitem); rs.Write(formitembytes, 0, formitembytes.Length); } rs.Write(boundarybytes, 0, boundarybytes.Length); string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: {2}\r\n\r\n"; string header = string.Format(headerTemplate, paramName, file, contentType); byte[] headerbytes = System.Text.Encoding.UTF8.GetBytes(header); rs.Write(headerbytes, 0, headerbytes.Length); FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read); byte[] buffer = new byte[10485760]; int bytesRead = 0; while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0) { rs.Write(buffer, 0, bytesRead); } fileStream.Close(); byte[] trailer = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n"); rs.Write(trailer, 0, trailer.Length); rs.Close(); WebResponse wresp = null; try { wresp = wr.GetResponse(); Stream stream2 = wresp.GetResponseStream(); StreamReader reader2 = new StreamReader(stream2); result = reader2.ReadToEnd(); } catch (Exception ex) { if (wresp != null) { wresp.Close(); wresp = null; } } finally { wr = null; } return result; } #endregion
View Code

     這裏有點奇怪啊,開始我是經過PostMan直接操做的,抓取了提交的參數等信息,而後在後臺去拼這個請求,跟PostMan提交的參數徹底一致,但是百度返回的提示卻一直都是少參數,無奈之下只能放棄最初本身寫的,採用了上邊的那種方式,不過好在最終目的仍是實現了。app

     手動同步用了一段時間以後,就發現問題了,每當須要同步數據的時候,都得點一下,這樣領導就不高興了。莫非我要叫我的實時去點這個按鈕麼?好吧,我作個自動同步的工具。這裏說的自動同步工具,其實就是隔一段時間就本身去同步一下數據,隔一段時間去同步一下數據。這裏最初也想了兩個方案,一個就是作一個頁面,隔一段時間向後臺發請求,檢查是否有可更新的數據,若是有能夠更新的數據,就調一下上邊的方法。這樣作的優點是改動量最小,只須要加一個頁面而後寫個定時器就搞定了,但是缺點就是若是有多我的同時打開這個頁面,數據就會混亂。另外一個方案就是建一個Windows服務,按期去跑這個同步數據的代碼。這樣作的優點是不依賴於IIS,穩定。最終執行的方案也是後者,經過構建windows服務來作這個事情。

     構建Windows服務這裏就不贅述了,網上有不少示例,基本上都是一搜一大把。在服務裏,咱們每隔5分鐘去跑一次自動同步的代碼,這樣就實現了數據自動同步功能。關於百度LBS數據同步這塊兒到此結束。這兩天正在作一個新用戶註冊特效和用戶下單特效。大體需求就是在如下這個雲麻點地圖上,一旦有新用戶註冊,以動態圖片顯示出他的座標位置信息,用戶下單也是同樣,每一個用戶都是一個雲麻點,一旦某個用戶下單成功,展現一個煙花綻開的特效。基本功能都作得差很少了,哪天有時間再更新上來!

相關文章
相關標籤/搜索