PC端直接获取QQ邮箱中转站&超星云盘分享链接小程序

https://storage.live.com/items/5C7B791DA27C1639!146?authkey=AHkU_JrotO2jrP8
QQ邮箱中转站和超星云盘都都可以通过手机客户端获取到文件分享链接,但是从PC端无法直接生成分享链接!
每次获取分享链接都还需要去手机端操作,比较繁琐,要分享的文件一多更让人头皮发麻,于是写了这个小程序自用。


一、功能说明

功能实现的过程很菜鸡,以后考虑优化代码。
  1. 将QQ邮箱中转站的复制的下载链接转换为分享链接
  2. 通过模拟键盘操作,获取超星云盘文件的文件ID,靠此生成分享链接
在QQ邮箱中转站右键下载链接——复制链接地址,然后点击软件直接转换即可。
超星云盘需要用浏览器的F12(开发者工具),获取需要分享的文件的class字段,再点击软件获取分享链接。
具体操作是:F12——选择网页元素——双击右方网页源代码——默认就会选中class=“fileName node_name_xxxxxx”字段,这时再用程序转换即可。
同时软件可以实现模拟键盘来实现上方操作:
将鼠标放在超星云盘的文件名上(注意,一定要放在文件名上),再按Ctrl+Q,即会自动模拟上方的操作,并生成分享链接。

二、功能的实现过程

C#的80%都忘了,很简单的代码,还是花了半天的时间来百度才解决,整理一下解决这个问题的过程。

1.解决思路

QQ邮箱链接转换

对于如何转换QQ邮箱下载链接,搜到了这一篇博文:文件上传到QQ邮箱文件中转站,链接
原文:
首先要学会分析下载地址,其实很简单,首先看下面的连接
http://mail.qq.com/cgi-bin/ftnExs_download?k=&t=exs_ftn_download&code=(提取文件下载地址格式)
文件中转站提取文件下载地址需要2个要素,一个是k 一个是code,k是唯一编码 code是提取码(k和code码从你的文件中转站可以找到:点下载,右键“直接下载”复制k=后面的编码),比如中转站上一个文件的
  • k=723265618f77969c56fb5d774463504c510152045b05045019530659544e5607040048000301524e070a0750540251565151520062716231555b0b18422117101461110e124d0f130732
  • code=42eabcbc
那么只需要把下面的链接发给好友,好友就可以下载了
http://mail.qq.com/cgi-bin/ftnExs_download?k=723265618f77969c56fb5d774463504c510152045b05045019530659544e5607040048000301524e070a0750540251565151520062716231555b0b18422117101461110e124d0f130732&t=exs_ftn_download&code=42eabcbc,这个链接跟你发给好友邮件里面的链接是一样的
获取分享链接需要一个k值和一个code值,k是唯一编码,code是提取码(k和code码从你的文件中转站可以找到:右键在下载按钮上复制链接,即可得到一个包含K值和code值的链接)
20190505182242.png
所以只需要截取字符串中的K值和code值,并合并成一个新链接即可。

超星云盘链接转换

通过观察从手机端获取的分享链接,不难发现,超星云盘的分享页面链接很简单,就是 https://pan-yz.chaoxing.com/external/m/file/加了一个文件ID,而这个ID在网页端通过F12开发者模式可以很容易获取到,在a标签的class里面。
20190505182107.png
这里我能想到的办法是:C#模拟键盘操作,自动打开网页开发者模式,并自动复制这段class,然后通过代码截取字符段最后拼接得到一个分享链接。

2.关于转换链接需要的C#知识如下

根据单个分隔字符用split截取

这里我参考了这篇教程:C#几种截取字符串的方法小结
string st="GT123_1";
string[] sArray=st.split("_");
即可得到sArray[0]=”GT123”,sArray[1]=”1”;

根据字符串或字符串组来截取字符串

string str = "GTAZB_JiangjBen_123";
string[] sArray = str.Split( new string[]{"Ji","jB"}, StringSplitOptions.RemoveEmptyEntries);
foreach(string e in sArray)
{
Console.WriteLine(e);
}
得到sArray[0]=”GTAZB_”,sArray[1]=”ang”,sArray[2]=”en_123”;
这里后面又涉及到一个知识,想定义一个包含引号的字符串要用到\转移字符,比如:" \" "

获取&设置剪贴板数据

同时为了简化操作步骤(直接获取剪贴板数据—>点击转换),参考了这篇教程:c#操作windows剪贴板总结
一、获取剪贴板数据:
private void button1_Click(object sender, System.EventArgs e) 
{ 
   // Create a new instance of the DataObject interface. 
   IDataObject data = Clipboard.GetDataObject(); 
   // If the data is text, then set the text of the TextBox to the text in the Clipboard. 
   if (data.GetDataPresent(DataFormats.Text)) 
      textBox1.Text = data.GetData(DataFormats.Text).ToString(); 
}
二、将数据放到剪贴板上:
private void button1_Click(object sender, System.EventArgs e) 
{ 
   Clipboard.SetDataObject(textBox1.Text); 
}
然而上面这个办法有点问题,通过 button_Click 事件重复获取是没问题;但如果后面,我通过模拟键盘Ctrl+C重复获取剪贴板数据的话,在第二次获取的时候,就会报错,几经折腾发现是线程的问题。

1

因为剪切板只能在单线程单元中访问,所以我们必须让它在ApartmentState.STA的一个线程中中运行。
参考了这几个教程:
  1. 如何将字符串的内容复制到C#中的剪贴板?
  2. C# 线程间不能调用剪切板的问题
  3. C#操作剪切板(Clipboard)
  4. c#在线程中调用Clipboard.SetImage(pictureBox1.Image)出错
最后我写出的代码如下:
 public void somethingToRunInThread()
        {
            string sharelink;
            string Share1 = "https://pan-yz.chaoxing.com/external/m/file/";

            string originallink = Clipboard.GetText();
            //根据特定字符:"node_name_"和"\" style"分割字符串,(\"是转义字符)
            string[] chaoxin = originallink.Split(new string[] { "node_name_", "\" style" }, StringSplitOptions.RemoveEmptyEntries);
            sharelink = Share1 + chaoxin[1];
            Clipboard.SetText(sharelink);
            notifyIcon1.ShowBalloonTip(200, "链接转换成功并已复制到剪贴板", sharelink, ToolTipIcon.None);
        }

        public void GetClipboard()
        {
            Thread myThread = new Thread(somethingToRunInThread);
            //注意,一般启动一个线程的时候没有这句话,但是要操作剪切板的话这句话是必需要加上的,因为剪切板只能在单线程单元中访问,这里的STA就是指单线程单元
            myThread.SetApartmentState(ApartmentState.STA);
            myThread.Start();
        }
然后在后文的模拟键盘操作中直接调用GetClipboard()这个方法,则会启动somethingToRunInThread()线程,运行链接转换的代码。
对,就这么简单的一回事,我之前折腾了一天都没解决!!!

判断字符串内容`

把两个链接转换功能集中到一个按钮事件上,用了IF来判断,这里参考了:C#判断一个字符串是否包含另一个字符串?

3.最后关于链接转换的主要代码如下

        private void convert_Click(object sender, EventArgs e)
        {
        string Share1 = "https://pan-yz.chaoxing.com/external/m/file/";
        string Share2 = "http://mail.qq.com/cgi-bin/ftnExs_download?k=";
        string Share3 = "&t=exs_ftn_download&code=";
        string originallink = "null";
        string sharelink;

        //获取剪贴板数据
        IDataObject data = Clipboard.GetDataObject();
        if (data.GetDataPresent(DataFormats.Text))
            originallink = data.GetData(DataFormats.Text).ToString();

        if (originallink.Contains("fileName"))
        {
            string[] chaoxin = originallink.Split(new char[1] { '_' });
            sharelink = Share1 + chaoxin[2];
            Clipboard.SetDataObject(sharelink);
        }
        if (originallink.Contains("https://mail.qq.com/"))
        {
            string[] QQmail = originallink.Split(new string[] { "code=", "&k=" }, StringSplitOptions.RemoveEmptyEntries);
            sharelink = Share2 + QQmail[2] + Share3 + QQmail[1];
        //把数据放到剪贴板上
            Clipboard.SetDataObject(sharelink);
        }
        MessageBox.Show("链接转换成功并已复制到剪贴板", "复制成功");
    }

4.模拟键盘操作,获取超星云盘的文件class所需要的知识如下

模拟鼠标键盘

我用了博主说的第二种方式:keybd_event

模拟组合键:CTRL + A

        [DllImport("user32.dll", EntryPoint = "keybd_event", SetLastError = true)]
        public static extern void keybd_event(Keys bVk, byte bScan, uint dwFlags, uint dwExtraInfo);
          public const int KEYEVENTF_KEYUP = 2;

        private void button1_Click(object sender, EventArgs e)
        {
            webBrowser1.Focus();
            keybd_event(Keys.ControlKey, 0, 0, 0);
            keybd_event(Keys.A, 0, 0, 0);
            keybd_event(Keys.ControlKey, 0, KEYEVENTF_KEYUP, 0);
        }

如何引入DllImport

这里涉及到一个引入DllImport的知识,我参考了这篇文章:C#中DllImport用法汇总
20190505183644.png
代码:using System.Runtime.InteropServices;
DllImport 是 System.Runtime.InteropServices命名空间下的一个属性类,因此ASP.NET中要使用DllImport的,必须在先“using System.Runtime.InteropServices;”。其功能是提供从非托管DLL导出的函数进行调用所必需的信息。DllImport 属性应用于方法,要求最少要提供包含入口点的dll的名称。

延时函数

为了在模拟键盘过程中给予浏览器反应时间,所以需要加入延迟,不然运行过程会各种出错。我参考这篇教程:C#一些延时函数

实现全局快捷键

最后,我想在全局范围内实现快捷键运行程序,几经搜索,采用了这个博主的办法:C#全局键盘GetAsyncKeyState

添加成功提示

我想在链接转换成功之后,添加一个自动提醒并消失的弹窗,几经搜索,最后用了这篇帖子中,一个老哥的办法,通过NotifyIcon控件(NotifyIcon是使用的托盘图标控件)来实现:c# 如何实现类似QQ方式的消息提醒
private void button1_Click(object sender, EventArgs e)
        {
            notifyIcon1.ShowBalloonTip(500, "标题", "内容内容", ToolTipIcon.Info);
        }

操作步骤:

1:拖一个NotifyIcon到窗口编辑界面上;
2:点击NotifyIocn图标右上角的小三角,再点击:ChooseIcon选择一个图标文件。注意:必须,否则无提示。
3:拖一个按钮控件,双击。
4:在按钮事件中添加notifyIcon1.ShowBalloonTip(500, “标题”, “内容内容”, ToolTipIcon.Info);
  • 一定要操作步骤2,否则将没有提示。
  • 至于ShowballoonTip各个参数的意思分别是:显示时长,标题,内容,显示的图标(内部用的,可以是其它的,请参阅MSDN)。

为了使NotifyIcon控件在软件退出后也不占用系统空间

4.最后模拟键盘的代码如下

//模拟键盘事件
        [DllImport("user32.dll", EntryPoint = "keybd_event", SetLastError = true)]
        public static extern void keybd_event(Keys bVk, byte bScan, uint dwFlags, uint dwExtraInfo);
        public const int KEYEVENTF_KEYUP = 2;

        //模拟鼠标事件
        [System.Runtime.InteropServices.DllImport("user32")]
        private static extern int mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);
        //移动鼠标 
        const int MOUSEEVENTF_MOVE = 0x0001;
        //模拟鼠标左键按下 
        const int MOUSEEVENTF_LEFTDOWN = 0x0002;
        //模拟鼠标左键抬起 
        const int MOUSEEVENTF_LEFTUP = 0x0004;
        //模拟鼠标右键按下 
        const int MOUSEEVENTF_RIGHTDOWN = 0x0008;
        //模拟鼠标右键抬起 
        const int MOUSEEVENTF_RIGHTUP = 0x0010;
        //模拟鼠标中键按下 
        const int MOUSEEVENTF_MIDDLEDOWN = 0x0020;
        //模拟鼠标中键抬起 
        const int MOUSEEVENTF_MIDDLEUP = 0x0040;
        //标示是否采用绝对坐标 
        const int MOUSEEVENTF_ABSOLUTE = 0x8000;

        //导入dll文件
        [DllImport("user32.dll")]
        //函数声明
        public static extern int GetAsyncKeyState(int vKey);
        //时钟监控事件 Interval =100
        private void timer1_Tick(object sender, EventArgs e)
        {
            //需要监控什么按键就写某个按键的ascii码
            if (GetAsyncKeyState(17) != 0 & GetAsyncKeyState(81) != 0)
            {
                Clipboard.Clear();
                //模拟鼠标右键
                mouse_event(MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0);
                System.Threading.Thread.Sleep(300);
                //模拟Ctrl+Shift+I
                keybd_event(Keys.N, 0, 0, 0);
                keybd_event(Keys.N, 0, KEYEVENTF_KEYUP, 0);
                //延时1S
                System.Threading.Thread.Sleep(1200);
                //模拟F2
                keybd_event(Keys.F2, 0, 0, 0);
                keybd_event(Keys.F2, 0, KEYEVENTF_KEYUP, 0);
                System.Threading.Thread.Sleep(300);
                //模拟Ctrl+A
                keybd_event(Keys.ControlKey, 0, 0, 0);
                keybd_event(Keys.A, 0, 0, 0);
                keybd_event(Keys.ControlKey, 0, KEYEVENTF_KEYUP, 0);
                keybd_event(Keys.A, 0, KEYEVENTF_KEYUP, 0);
                System.Threading.Thread.Sleep(300);
                //模拟Ctrl+C
                keybd_event(Keys.ControlKey, 0, 0, 0);
                keybd_event(Keys.C, 0, 0, 0);
                keybd_event(Keys.ControlKey, 0, KEYEVENTF_KEYUP, 0);
                keybd_event(Keys.C, 0, KEYEVENTF_KEYUP, 0);
                System.Threading.Thread.Sleep(500);

                //调用GetClipboard()方法运行线程
                GetClipboard();
            }

            }

窗口置顶

简单通过CheckBox来判断是否让窗口置顶,在Form1的Deactivate事件中写入如下代码
    private void Form1_Deactivate(object sender, EventArgs e)
    {
        if (TopcheckBox.Checked == true)
        {
            this.TopMost = true;
        }
        else
        {
            this.TopMost = false;
        }
    }

固定窗口大小

  1. 把MaximizeBox和MinimumBox设置为false(消除最大最小化按钮)
  2. 把MaximumSize和MinimumSize设置成和Form的Size一样(此时鼠标放到窗体边缘仍然能看到调整大小的光标)
  3. AutoSizeMode改为GrowAndShrink,实现完全不能调整大小

二、下载链接等

程序下载地址:QQ邮箱中转站下载地址
一个很简单的问题,但还是花了很多时间,自己要学的还很多啊。

此博客中的热门博文