最近遇到要读取QQ相册的问题,所以自己诼磨了下,把读取相册的思路和代码写下来。
很多网站到图片链接都做了盗链处理。意思是如果你想在不是他的网站上引用图片就不会正确显示图片,而是一张其它的版权说明之类的图片。实现防盗链的一般做好是通过判断Request的Headers中的Referer属性。该属性保存着客户端在访问该链接之前的地址。又如我访问http://www.domain.com/,该网站中有一张图片<img src='http://www.qq.com/logo.gif'> ,该图片请求的Referer属性就应该是http://www.domain.com/ 表示是从该地址转过来的。
要突破这种防盗链,你可以在发请求这前修改Referer属性,不过这有点难度。因为这个属性是只读的。另外一种做法是用程序的方式(在ASP.NET中即HttpWebRequest)来新建一个请求,些时Referer是空的,所以防盗链也就不起作用。
QQ相册取图片的方式隐藏得很好,是用Ajax方式读取的,js脚本文件也进行了压缩,所以很难看清它是怎么取图片的。看了很久之后才发现它读相册以及相册中图片的地址:
相册地址:http://p{0}.photo.qq.com/{1}/16 //{0} = {1} % 13 + 1 {1}为qq号
图片地址:http://sz.photo.store.qq.com/http_staload.cgi?{0}/{1} ; //{0}qq号 {1}相册号
这两个地址只是返回一个XML文件,文件中才标识着真正的图片地址。
程序的思路是先通过QQ号得到相册相关信息的xml文件,从文件中得到相册号和相册封面图片地址,用HttpWebRequest得到该封面图片并显示出来。当用户点击该图片时再根据相册号得到该相册下图片信息的xml文件,再次用HttpWebRequest的方式显示预览图片,供用户选择要导入的图片,最好根据图片的真正地址把图片保存在当前文件夹中。
把程序贴在下面,一个面页GetPhoto.aspx和后台代码GetPhoto.aspx.cs
GetPhoto.aspx
用了两个DataList 一个用来显示相册图片,一个用来显示相册中的图片。
<%
...
@ Page language="c#" Codebehind="GetPhoto.aspx.cs" AutoEventWireup="false" Inherits="tt.GetPhoto"
%>
<!
DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
>
<
HTML
>
<
HEAD
>
<
title
>
GetPhoto
</
title
>
<
meta
content
="Microsoft Visual Studio .NET 7.1"
name
="GENERATOR"
>
<
meta
content
="C#"
name
="CODE_LANGUAGE"
>
<
meta
content
="JavaScript"
name
="vs_defaultClientScript"
>
<
meta
content
="http://schemas.microsoft.com/intellisense/ie5"
name
="vs_targetSchema"
>
</
HEAD
>
<
body
>
<
form
id
="Form1"
method
="post"
runat
="server"
>
<
P
>
QQ号码:
<
asp:textbox
id
="txtQQ"
runat
="server"
></
asp:textbox
>
<
asp:button
id
="Button1"
runat
="server"
Text
="读取相册"
></
asp:button
></
P
>
<
asp:Label
ID
="msg"
Runat
="server"
></
asp:Label
>
<
P
>
<
asp:DataList
id
="albumList"
Runat
="server"
RepeatColumns
="8"
EnableViewState
="False"
>
<
ItemTemplate
>
<
div
>
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
<
div
>
<%
...
# DataBinder.Eval(Container.DataItem,"PicLink")
%>
</
div
>
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
<
div
>
<%
...
# DataBinder.Eval(Container.DataItem,"PicTitle")
%>
</
div
>
</
div
>
</
ItemTemplate
>
</
asp:DataList
>
<
asp:DataList
id
="picList"
Runat
="server"
RepeatColumns
="8"
EnableViewState
="False"
DataKeyField
="PicUrl"
>
<
ItemTemplate
>
<
div
>
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
<
div
>
<%
...
# DataBinder.Eval(Container.DataItem,"PicLink")
%>
</
div
>
<
div
>
<
asp:CheckBox
ID
="confirm"
Runat
="server"
Text
='<%#
DataBinder.Eval(Container.DataItem,"PicTitle")%
>
' />
</
div
>
</
div
>
</
ItemTemplate
>
<
FooterTemplate
>
<
div
>
<
asp:Button
ID
="bntOk"
Runat
="server"
CommandName
="Update"
Text
="开始导入"
></
asp:Button
></
div
>
</
FooterTemplate
>
</
asp:DataList
>
</
P
>
</
form
>
</
body
>
</
HTML
>
后台代码GetPhoto.aspx.cs 稍微有点长
using
System;
using
System.Collections;
using
System.Data;
using
System.Web;
using
System.Web.SessionState;
using
System.Web.UI;
using
System.Web.UI.WebControls;
using
System.Web.UI.HtmlControls;
using
System.Net;
using
System.IO;
using
System.Xml;
using
System.Drawing.Imaging;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
namespace
tt
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
/**//// <summary>
/// GetPhoto 的摘要说明。
/// </summary>
public class GetPhoto : System.Web.UI.Page
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
protected System.Web.UI.WebControls.TextBox txtQQ;
protected System.Web.UI.WebControls.Button Button1;
protected System.Web.UI.WebControls.DataList picList; //{0}qq号
protected System.Web.UI.WebControls.Label msg;
protected System.Web.UI.WebControls.DataList albumList;
private const string QQPHOTO = "http://p{0}.photo.qq.com/{1}/16"; //{0} = {1} % 13 + 1 {1}为qq号
//取相册的另一个有用路径,在第一个可用的情况下用。
private const string QQPHOTO_B = "http://photo.qq.com/cgi-bin/common/cgi_list_album?uin={0}";
private const string ALBUMURL = "http://sz.photo.store.qq.com/http_staload.cgi?{0}/{1}"; //{0}qq号 {1}album号
private string UserID = "maxwell"; //用户名
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
private void Page_Load(object sender, System.EventArgs e)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
//点击“开始导入”时 把打勾的图片保存到本机上。
picList.UpdateCommand += new DataListCommandEventHandler(picList_UpdateCommand);
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
//取url参数中的相册图片 即取qq相册 QQ有防盗链
if(Request.QueryString["pre"] != null)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
string url = Request.QueryString["pre"];
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
WebRequest preR = WebRequest.Create(url);
Stream stream = preR.GetResponse().GetResponseStream();
//QQ相册只能上传jpg|gif|png的图片
//把这些图片保存为jpeg格式和ContentType设置为image/jpeg都能显示。
Response.ClearContent();
Response.ContentType = "image/jpeg";
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
System.Drawing.Bitmap img = new System.Drawing.Bitmap(stream);
img.Save(Response.OutputStream,ImageFormat.Jpeg);
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
img.Dispose();
stream.Close();
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
Response.End();
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
//根据相册ID取该相册中的图片。
if(Request.QueryString["id"] != null && Request.QueryString["qq"] != null)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
string id = Request.QueryString["id"];
string qq = Request.QueryString["qq"];
string albumUrl = string.Format(ALBUMURL,qq,id);
XmlDocument xml = GetXmlData(albumUrl);
if(xml == null)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
msg.Text = "暂时不能读取数据。请稍后再试。";
return;
}
XmlNodeList list = xml.GetElementsByTagName("pic");
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
PicAlbum[] pics = new PicAlbum[list.Count];
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
for(int i=0; i<list.Count; i++)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
string name = list[i].SelectSingleNode("name").InnerText;
string pre = list[i].SelectSingleNode("pre").InnerText;
string url = list[i].SelectSingleNode("url").InnerText;
string picLink = "<img src='" + Request.Url.AbsolutePath + "?pre=" + pre + "'/>";
pics[i] = new PicAlbum(url,picLink,name);
}
albumList.Visible = false;
picList.Visible = true;
picList.DataSource = pics;
picList.DataBind();
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/7ff8d92cded7e0ce15e7ca1acc870052.gif)
Web 窗体设计器生成的代码#region Web 窗体设计器生成的代码
override protected void OnInit(EventArgs e)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
//
// CODEGEN: 该调用是 ASP.NET Web 窗体设计器所必需的。
//
InitializeComponent();
base.OnInit(e);
}
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
/**//// <summary>
/// 设计器支持所需的方法 - 不要使用代码编辑器修改
/// 此方法的内容。
/// </summary>
private void InitializeComponent()
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
this.Button1.Click += new System.EventHandler(this.Button1_Click);
this.Load += new System.EventHandler(this.Page_Load);
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
}
#endregion
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
//读取相册
private void Button1_Click(object sender, System.EventArgs e)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
string qq = txtQQ.Text;
string photoUrl = string.Format(QQPHOTO,(int.Parse(qq)%13+1),qq);
//从QQ读取相册数据
XmlDocument xml = GetXmlData(photoUrl);
if(xml == null)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
//判断静态XML是否取到,如果未取到,再尝试拉一次CGI(QQ的说法)
string photoBackupUrl = string.Format(QQPHOTO_B,qq);
xml = GetXmlData(photoBackupUrl);
if(xml == null)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
msg.Text = "暂时不能读取数据。请稍后再试。";
return;
}
}
XmlNodeList list = xml.GetElementsByTagName("album");
ArrayList albums = new ArrayList();
for(int i=1; i<list.Count; i++)//从1开始,是因为xml文件的第一个album不是相册数据。
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
string priv = list[i].SelectSingleNode("priv").InnerText.Trim();
if(priv != "1")
continue;
string id = list[i].SelectSingleNode("id").InnerText;
string name = list[i].SelectSingleNode("name").InnerText;
string pre = list[i].SelectSingleNode("pre").InnerText;
string picLink = "<a href='"+ Request.Url.AbsolutePath + "?id=" + id +"&qq=" + qq + "'><img src='" + Request.Url.AbsolutePath + "?pre=" + pre + "'/></a>";
albums.Add(new PicAlbum(id,picLink,name));
}
picList.Visible = false;
albumList.Visible = true;
albumList.DataSource = albums;
albumList.DataBind();
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
/**//// <summary>
/// 根据地址得到一个xml文档
/// </summary>
/// <param name="url">地址</param>
/// <returns></returns>
private XmlDocument GetXmlData(string url)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
HttpWebRequest wreq = (HttpWebRequest)WebRequest.Create(url);
//wreq.Timeout = 20;
HttpWebResponse wres = null;
XmlDocument xml = null;
try
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
wres = (HttpWebResponse)wreq.GetResponse();
xml = new XmlDocument();
xml.Load(wres.GetResponseStream());
}
finally
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
wres.Close();
}
return xml;
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
/**//// <summary>
/// 开始导入图片 只把打勾的图片导入。
/// </summary>
/// <param name="source"></param>
/// <param name="e"></param>
private void picList_UpdateCommand(object source, DataListCommandEventArgs e)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
DataList picList = source as DataList;
PicAlbum[] pics = (PicAlbum[])picList.DataSource;
ArrayList urls = new ArrayList(); //用来保存要导入图片的地址。
for(int i=0; i<picList.Items.Count; i++)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
CheckBox cb = (CheckBox)picList.Items[i].FindControl("confirm");
if(cb.Checked)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
urls.Add(picList.DataKeys[i]);
}
}
//读取要保存图片的URL后,保存图片
SavePics(urls);
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
/**//// <summary>
/// 保存列表中的图片。
/// </summary>
/// <param name="urls">保存图片地址的列表</param>
private void SavePics(ArrayList urls)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
DateTime date = DateTime.Now;
string path = Server.MapPath(".");
int i=1;
foreach(string url in urls)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
string name = path + "/" + UserID + date.ToString("yyyyMMdd") + date.Hour.ToString()
+ date.Minute.ToString() + date.Second.ToString()
+ (i++)
+ ".jpg";
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
Stream stream = req.GetResponse().GetResponseStream();
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
System.Drawing.Bitmap img = new System.Drawing.Bitmap(stream);
img.Save(name,ImageFormat.Jpeg);
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
img.Dispose();
stream.Close();
}
msg.Text = "文件保存完成。";
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
/**//// <summary>
/// 图片相册类
/// </summary>
public class PicAlbum
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
private string picUrl;
private string picLink;
private string picTitle;
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
public PicAlbum(string picurl,string piclink,string pictitle)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
picUrl = picurl;
picLink = piclink;
picTitle = pictitle;
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
public string PicUrl
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
get...{ return picUrl;}
}
public string PicLink
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
get...{ return picLink;}
}
public string PicTitle
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
get...{ return picTitle;}
}
}
}