MarkDown本地圖片上傳工具製做總結

引言:開始嘗試使用MarkDown語法寫文檔,發現圖片必須用外鏈的形式才能插入到文章中,而本身平時最經常使用的插入圖片方式就是QQ截屏,以爲很不方便因此製做的小工具輔助上傳,由於時間和水平有限,其實代碼寫的很粗糙,之後有時間會不斷改進。web

http抓包和分析

上傳用的網址是極簡圖牀 極簡圖牀,這個圖牀內部使用的是六牛雲,由於這個圖牀的上傳不須要身份認證比較簡單,因此就用它作的,後期仍是要換到六牛雲比較保險。編程

抓包使用的是Fiddler 4 ,開啓以後至關於一個代理,電腦幾乎全部的網絡請求都會被監視到,可是瀏覽器的包須要設置一下代理,IP寫本機,端口號8888

== 注意 ==:這個代理設置會影響到IE瀏覽器,若是關掉了fiddler沒有刪掉代理,容易出現的一個奇葩問題就是TFS沒法登錄。windows

抓包的結果是這樣的:

數據包裏面有幾個須要注意的點:數組

  • url的參數裏面包含了圖片的格式
  • 上傳須要帶Cookie才能成功
  • 圖片不是以那種表單的形式上傳,而是直接把base64編碼的字符串放到了http的主體裏面

httpwebrequest模擬post

在這裏面我遇到了一個問題,就是cookie的獲取,我用網上給的那種在爬蟲程序中獲取cookie 的方式並無成功,後來我想這種字符串類型的cookie不就是http頭裏面的一部分信息麼,而後就把抓包裏面的cookie直接以鍵值對的方式寫到了http頭裏面,這個也不知道之後會有什麼弊端,代碼以下:瀏覽器

public static string PostHttp(string url, byte[] body)
        {
            HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(url);

            httpWebRequest.ContentType = "text/plain";
            httpWebRequest.Method = "POST";
            httpWebRequest.Timeout = 20000;
            httpWebRequest.Headers.Add("Cookie", cookieStr);

            byte[] btBodys = body;
            httpWebRequest.ContentLength = btBodys.Length;
            httpWebRequest.GetRequestStream().Write(btBodys, 0, btBodys.Length);


            HttpWebResponse httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse();
            StreamReader streamReader = new StreamReader(httpWebResponse.GetResponseStream());
            string responseContent = streamReader.ReadToEnd();


            httpWebResponse.Close();
            streamReader.Close();
            httpWebRequest.Abort();
            httpWebResponse.Close();

            return responseContent;
        }

HttpClient,HttpWebRequest,HttpWebResponse這三個類是封裝的http請求的類,其中HttpClient的封裝程度最高,關於這三個類的具體使用和注意事項準備再寫一個總結。cookie

返回的值是Json形式,這個在Fiddler抓的包裏面能看見,用Json.Net解析一下就能夠拿到外鏈字符串了。網絡


監聽剪貼板

這個是目前來講我以爲比較麻煩的地方,由於涉及到了對windows這個操做系統的編程,好比剪貼板的變化,或者快捷鍵(ctrl+v)的監聽,這些有一部分要調用windows操做系統的API,更有一些須要使用一種叫HOOK的技術,甚至能夠hook住QQ截屏(ctrl+alt+a)這個函數的地址,這個應該是實現效果的最優解。ide

可是由於本身水平有限,因此選擇了最簡單的調用Windows API的實現方式。函數

1·剪貼板介紹

剪貼板是很是方便的進程間通信,下面是引用的一段解釋工具

剪貼板是Windows系統一段可連續的。可隨存放信息的大小而變化的內存空間,用來臨時存放交換信息。內置在windows而且使用系統的內部資源RAM,或虛擬內存來臨時保存剪切和複製的信息,能夠存放的信息種類是多種多樣的。剪切或複製時保存在剪貼板上的信息,只有再剪貼或複製另外的信息,或停電、或退出windows,或有意地清除時,纔可能更新或清除其內容,即剪貼或複製一次,就能夠粘貼屢次。

2·剪貼板引用

C#定義了一個類System.Windows.Forms.Clipboard來簡化剪切板操做,要使用剪貼板要先引入三個函數:

[System.Runtime.InteropServices.DllImport("user32")]
        private static extern IntPtr SetClipboardViewer(IntPtr hwnd);
        [System.Runtime.InteropServices.DllImport("user32")]
        private static extern IntPtr ChangeClipboardChain(IntPtr hwnd,IntPtr hWndNext);
        [System.Runtime.InteropServices.DllImport("user32")]
        private static extern int SendMessage(IntPtr hwnd,int wMsg,IntPtr wParam,IntPtr lParam);

還有兩個常量:

const int WM_DRAWCLIPBOARD = 0x308;
 const int WM_CHANGECBCHAIN = 0x30D;
  • IntPtr SetClipboardViewer(IntPtr hwnd)
    用於往觀察鏈中添加一個窗口句柄,這個窗口就可成爲觀察鏈中的一員了,返回值指向下一個觀察者。

  • IntPtr ChangeClipboardChain(IntPtr hwnd,IntPtr hWndNext): 刪除由hwnd指定的觀察鏈成員,這是一個窗口句柄,第二個參數hWndNext是觀察鏈中下一個窗口的句柄

  • int SendMessage(IntPtr hwnd,int wMsg,IntPtr wParam,IntPtr lParam): 發送消息,還有一個很重要的做用是將WM_DRAWCLIPBOARD 消息傳遞到下一個觀察鏈中的窗口

觀察鏈:其實有不少程序在一塊兒監視剪貼板,好比迅雷在檢測到你複製了下載連接的時候,就會自動啓動。

3·剪貼板編程

定義完成後,能夠分三部來使用,第一步把本身的窗口添加到觀察鏈中成爲觀察者,並保存下一個觀察者的句柄;第二步監視剪切板,並把剪切板變化的消息發送給下一個觀察者;第三步撤消本身定義的觀察者,並通知下一個觀察者。

第一步:把本身的窗口添加到觀察鏈中成爲觀察者,並保存下一個觀察者的句柄
//存放觀察鏈中下一個窗口句柄   
IntPtr NextClipHwnd;

private void Form1_Load(object sender, System.EventArgs e)
{  
     //得到觀察鏈中下一個窗口句柄
    NextClipHwnd=SetClipboardViewer(this.Handle);           
 }
第二步:監視剪切板,並把剪切板變化的消息發送給下一個觀察者,這裏須要重載WndProc方法;

這裏用到兩個消息常量:

const int WM_DRAWCLIPBOARD = 0x308;
  const int WM_CHANGECBCHAIN = 0x30D;

protected override void WndProc(ref System.Windows.Forms.Message m)
{
    switch(m.Msg)
    {
        case WM_DRAWCLIPBOARD:
            //將WM_DRAWCLIPBOARD消息傳遞到下一個觀察鏈中的窗口
            SendMessage(NextClipHwnd,m.Msg,m.WParam,m.LParam);
            IDataObject iData = Clipboard.GetDataObject(); 
              //檢測文本
            if(iData.GetDataPresent(DataFormats.Text)|iData.GetDataPresent(DataFormats.OemText))
            {
                     this.richTextBox1.Text=(String)iData.GetData(DataFormats.Text);
            }
             //檢測圖像
             if (iData.GetDataPresent(DataFormats.Bitmap))
              {
                        pictureBox1.Image=Clipboard.GetImage();
                        NewClipData();
              }
              //檢測自定義類型
               if (iData.GetDataPresent("myFormat"))
                {
                        MyObj myobj=(MyObj)iData.GetData("myFormat");
                        this.richTextBox1.Text=myobj.ObjName;
                }
              break;
        default:
            base.WndProc(ref m);
            break;
    }       
}
第三步:撤消本身定義的觀察者,並通知下一個觀察者。
private void Form1_Closed(object sender, System.EventArgs e)
{
            //從觀察鏈中刪除本觀察窗口(第一個參數:將要刪除的窗口的句柄;第二個參數://觀察鏈中下一個窗口的句柄 )
           ChangeClipboardChain(this.Handle,NextClipHwnd);
            //將變更消息WM_CHANGECBCHAIN消息傳遞到下一個觀察鏈中的窗口
            SendMessage(NextClipHwnd,WM_CHANGECBCHAIN,this.Handle,NextClipHwnd); 
}

我本身的代碼並不是徹底按照上面的格式書寫。


讀取圖片而後轉碼

這一部分主要是編碼,字節數組和流的操做,就直接貼代碼了。

//讀取圖片而且進行操做
        private void Handler()
        {

            Image image = Clipboard.GetImage();
            if (image != null)
            {
                string base64str = ImageToBase64String(image);
                byte[] decodedByteArray = Encoding.UTF8.GetBytes(base64str);
                string responsdata = PostHttp(uri, decodedByteArray);
                ImageInfo info = JsonConvert.DeserializeObject<ImageInfo>(responsdata);
                Clipboard.SetDataObject("![](" + info.imageUrl + ")");
            }
        }
//圖片轉base64編碼
        private string ImageToBase64String(Image imageData)
        {
            string base64;
            MemoryStream memory = new MemoryStream();
            imageData.Save(memory, ImageFormat.Png);
            base64 = System.Convert.ToBase64String(memory.ToArray());
            memory.Close();
            memory = null;
            return base64;
        }

一些知識點的總結

  • [DllImport("user32.dll", CharSet = CharSet.Auto)]:這是導入Windows系統自帶的user32.dll中的函數(API),也能夠用於C++編寫的帶有導出函數的DLL
  • private IntPtr nextClipboardViewer :C#中的IntPtr類型稱爲「平臺特定的整數類型」,它們用於本機資源,如窗口句柄。資源的大小取決於使用的硬件和操做系統,但其大小老是足以包含系統的指針(所以也能夠包含資源的名稱)。 因此,在您調用的API函數中必定有相似窗體句柄這樣的參數,那麼當您聲明這個函數時,您應該將它顯式地聲明爲IntPtr類型。
  • 句柄:句柄,是整個Windows編程的基礎。一個句柄是指使用的一個惟一的整數值,即一個4字節(64位程序中爲8字節)長的數值,來標識應用程序中的不一樣對象和同類中的不一樣的實例,諸如,一個窗口,按鈕,圖標,滾動條,輸出設備,控件或者文件等。應用程序可以經過句柄訪問相應的對象的信息,可是句柄不是指針,程序不能利用句柄來直接閱讀文件中的信息。若是句柄不在I/O文件中,它是毫無用處的。 句柄是Windows用來標誌應用程序中創建的或是使用的惟一整數,Windows大量使用了句柄來標識對象。

結語

作的工具雖然小,可是涉及到了好多方面的知識,其中Windows編程這一塊是新的領域,http是加深了理解,IO操做和Image操做是學過可是已經不熟悉了,接下來的時間查缺補漏,而後把東西好好完善一下。

下載連接下載地址

相關文章
相關標籤/搜索