MarkDown本地图片上传工具制作总结
引言:开始尝试使用MarkDown语法写文档,发现图片必须用外链的形式才能插入到文章中,而自己平时最常用的插入图片方式就是QQ截屏,觉得很不方便所以制作的小工具辅助上传,因为时间和水平有限,其实代码写的很粗糙,以后有时间会不断改进。
http抓包和分析
上传用的网址是极简图床 极简图床,这个图床内部使用的是六牛云,因为这个图床的上传不需要身份认证比较简单,所以就用它做的,后期还是要换到六牛云比较保险。
抓包使用的是Fiddler 4 ,开启之后相当于一个代理,电脑几乎所有的网络请求都会被监视到,但是浏览器的包需要设置一下代理,IP写本机,端口号8888

== 注意 ==:这个代理设置会影响到IE浏览器,如果关掉了fiddler没有删掉代理,容易出现的一个奇葩问题就是TFS无法登陆。
抓包的结果是这样的:

数据包里面有几个需要注意的点:
- 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的封装程度最高,关于这三个类的具体使用和注意事项准备再写一个总结。
返回的值是Json形式,这个在Fiddler抓的包里面能看见,用Json.Net解析一下就可以拿到外链字符串了。
监听剪贴板
这个是目前来说我觉得比较麻烦的地方,因为涉及到了对windows这个操作系统的编程,比如剪贴板的变化,或者快捷键(ctrl+v)的监听,这些有一部分要调用windows操作系统的API,更有一些需要使用一种叫HOOK的技术,甚至可以hook住QQ截屏(ctrl+alt+a)这个函数的地址,这个应该是实现效果的最优解。
但是因为自己水平有限,所以选择了最简单的调用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("");
}
}
//图片转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操作是学过但是已经不熟悉了,接下来的时间查缺补漏,然后把东西好好完善一下。
下载链接:下载地址
MarkDown本地图片上传工具制作总结的更多相关文章
- 本地图片上传到GitHub,MarkDown使用Github图片地址
最近在学习用markdown编辑器,我是直接用有道云笔记编辑的,感觉真的好好用,编辑了一半的博客,可以按样式保存在云笔记中,我再也不会忘记写博客了~~ 但是在编辑博客的时候发现了一个问题,那就是本地图 ...
- JavaScript实现本地图片上传前进行裁剪预览
本项目支持IE8+,测试环境IE8,IE9,IE10,IE11,Chrome,FireFox测试通过 另:本项目并不支持Vue,React等,也不建议,引入JQuery和Vue.React本身提倡的开 ...
- 【咸鱼教程】本地图片上传。动态GIF表情图生成
本案例参考:http://emoji.decathlon.trustingme.cn/但是实现方式不一样. 教程目录一 head first二 打开本地图片功能三 拖拽和缩放手势,调整图片四 gifj ...
- TinyMCE的使用(包括汉化及本地图片上传功能)
TinyMCE我就不多介绍了,这是下载地址:https://www.tinymce.com/download/ 下载下来是英文版,要汉化也很简单.首先去网上随便下载个汉化包,然后把汉化包解压后的lan ...
- PHP 图片上传工具类(支持多文件上传)
====================ImageUploadTool======================== <?php class ImageUploadTool { private ...
- 本地图片上传与H5适配知识
最近用到本地图片上传作为API的参数,在网上看了许多,记录一下,以后可能用的着(仅自己记录用,看不清请绕路) function getObjectURL(file) { var url = null ...
- python实现本地图片上传到服务区
本地图片上传到服务器,其本质上来讲,就是读取本地图片,复制到服务器,并返回服务器url 前端代码,用的form表单提交,form表单中包含两个文件选择表单元素,选择文件,点击提交按钮,提交form表单 ...
- JS兼容各个浏览器的本地图片上传即时预览效果
JS兼容各个浏览器的本地图片上传即时预览效果 很早以前 在工作曾经碰到这么一个需求,当时也是纠结了很久,也是google了很久,没有碰到合适的demo,今天特意研究了下这方面的的问题,所以也就做了个简 ...
- AFNetworking网络请求与图片上传工具(POST)
AFNetworking网络请求与图片上传工具(POST) .h文件 #import <Foundation/Foundation.h> /** 成功Block */ typedef vo ...
随机推荐
- 动力IT教育背后的“神秘力量”
IT行业作为当前就业形势最好的行业,成为大多数毕业生以及转行人群的首选.且国家也对互联网大力支持,IT行业市场需求空缺也越来越大,随之IT培训机构也如雨后春笋般,层出不穷. 行业易选,但该如何选择培训 ...
- linux—粘滞位的设置
粘滞位(Stickybit),或粘着位,是Unix文件系统权限的一个旗标.最常见的用法在目录上设置粘滞位,如此以来,只有目录内文件的所有者或者root才可以删除或移动该文件.如果不为目录设置粘滞位,任 ...
- Java实现缓存(LRU,FIFO)
现在软件或者网页的并发量越来越大了,大量请求直接操作数据库会对数据库造成很大的压力,处理大量连接和请求就会需要很长时间,但是实际中百分之80的数据是很少更改的,这样就可以引入缓存来进行读取,减少数据库 ...
- PPT里面实现动态图表
今天又被学生提问,可不可以在PPT中做类似Excel中的动态图表?Excel里实现动态图表的方法通常是将图表关联到一个固定的数据区域,然后通过其他单元格的值或者控件来改变该固定区域的数据,以达到动态改 ...
- 老李分享:Android性能优化之内存泄漏2
这种创建Handler的方式会造成内存泄漏,由于mHandler是Handler的非静态匿名内部类的实例,所以它持有外部类Activity的引用,我们知道消息队列是在一个Looper线程中不断轮询处理 ...
- Myeclipse8.5开发-程序发布
1.新建focus.xml文件. 2.添加如下内容 <Context path="/focus" docBase="F:\Workspaces\MyEcli ...
- Adroid学习之 从源码角度分析-禁止使用回退按钮方案
有时候,不能让用户进行回退操作,如何处理? 查看返回键触发了哪些方法.在打开程序后把这个方法禁止了. 问题:程序在后台驻留,这样就会出现,其他时候也不能使用回退按钮.如何处理,在onpase()时方法 ...
- UIScrollView的布局
一.UIScrollView的子控件布局不能跟其它的控件一样进行布局,因为UIScrollView会根据子控件计算出ContentSize的大小,那么我们应该如何进行布局呀. 遵循以下两点就行 1.参 ...
- Android中实现定时器的四种方式
第一种方式利用Timer和TimerTask 1.继承关系 java.util.Timer 基本方法 schedule 例如: timer.schedule(task, delay,period); ...
- 前端借助dom-to-image把HTML转成图片并通过ajax上传到服务器
之前接到了一个任务,把jsp中的table转成一个图片,保存在指定文件夹并显示在前端. 我的思路是:一.引用第三方js在前端把table转成图片 二.通过ajax把图片上传到服务器,保存在指定文件夹 ...