[转载]C#实用网页抓取 - yao2yao4 - 博客园

mikel阅读(1130)

[转载]实用网页抓取 – yao2yao4 – 博客园.

0、前言

本文主要介绍如何抓取网页中的内容、如何解决乱码问题、如何解决登录问题以及对所采集的数据进行处理显示的过程。效果如下所示:

 

 

1、下载网页并加载至HtmlAgilityPack

这里主要用WebClient类的DownloadString方法和HtmlAgilityPack中HtmlDocument类LoadHtml方法来实现。主要代码如下。

 

复制代码
var url = page == 1 ? "http://www.cnblogs.com/" : "http://www.cnblogs.com/sitehome/p/" + page;
var wc = new WebClient
            {
                BaseAddress = url,
                Encoding = Encoding.UTF8
            };
var doc = new HtmlDocument();
var html = wc.DownloadString(url);
doc.LoadHtml(html);
复制代码

 

2、解决乱码问题

在抓取cnbeta的时候,我发现用上述方法抓取的html是乱码,开始我以为是网页编码问题,结果发现html网页是UTF-8格式,编码一 致。最后发现原因是网页被压缩过,WebClient类不能处理被压缩过了网页,不过可以从WebClient类扩展出新的类,来支持网页压缩问题。核心 代码如下,使用时用XWebClient替换WebClient即可。

 

复制代码
public class XWebClient : WebClient
{protected override WebRequest GetWebRequest(Uri address)
    {
        var request = base.GetWebRequest(address) as HttpWebRequest;
        request.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;return request;
    }
}
复制代码

 

3、解决登录问题

某些网站的一些网页,需要登录才能查看,仅靠网址是没办法抓到的,这需要从html协议相关的知识了,不过这里不需要那么深的知识,先来一个具 体的例子,先用chrome打开博客园、用F12或右键点击“审查元素”打开“开发者工具/Developer Tools”,选择“网路/Network”选项卡,刷新网页,点击开发者工具中的第一个请求,如下图所示:

 

 

此时就可以看到刚才那次请求的请求头(Request Header)了,有兴趣的童鞋可以对照着http协议来查看每一个部分代表什么含义,而这里只关注其中的Cookie部分,这里包括了自动登录需要的信 息,而回到问题,我不仅需要url,还需要携带cookie,而WebClient对象是没有Cookie相关的属性的,这时候又要扩展 WebClient对象了。核心代码如下:

 

复制代码
public class XWebClient : WebClient
{
    public XWebClient()
    {
        Cookies = new CookieContainer();
    }
    public CookieContainer Cookies { get; private set; }
    protected override WebRequest GetWebRequest(Uri address)
    {
        var request = base.GetWebRequest(address) as HttpWebRequest;
        request.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
        if (request.CookieContainer == null)
        {
            request.CookieContainer = Cookies;
        }
        return request;
    }
}
复制代码

 

这里GetWebRequest函数中获得WebRequest中的CookieContainer是null,所以我暴露了一个 CookieContainer,用来添加Cookie,使用时调用其Add(new Cookie(string name, string value, string path, string domain))方法即可,这里path一般为“/”,domain为url,上图中的Cookie按分号分割,等号左边的就是name,右边的就是 value。把所有的Cookie添加进去后,就可以抓取登录后的网页了。

 

4、Html解析

这里使用HtmlAgilityPack的HtmlDocument对象的DocumentNode.SelectSingleNode方法来 选择元素,得到的HtmlNode对象取.Attributes[“href”].Value即得到属性值,取InnerText即得到 InnerText。

这里的SelectSingleNode方法是可以接收XPath作为参数的,而这可以大大简化解析难度。

在网页上的一个元素上悬停,右键点击“审查元素”,然后在被选中的那一块,右键点击“Copy XPath”,然后粘贴在SelectSingleNode方法的参数位置即可。对XPath感兴趣的童鞋,可以随便看看其它元素的XPath,观察 XPath的语法规则。如果找不到某个元素对应的html节点,可以点击开发者工具左上角的放大镜,并在网页上点击该元素,其html节点就自动被选中 了。

 

5、对采集的数据进行过滤和排序

这里用Linq to Objects就可以,这里是最有个性化的步骤,以博客园为例,可以对发布时间、点击数、顶的数目、评论数、top N等等进行过滤或排序,甚至对某某人进行屏蔽,非常自由。

我最后筛选出数据有三个属性:Text,为显示的文本,可以包含评论数、发表时间、标题之类的信息;Summary:为鼠标悬停时提示的文本;Url:为点击链接后用浏览器打开的网址。

 

6、显示

我采用Wpf作为UI,代码如下:

 

复制代码
<Window x:Class="NewsCatcher.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="720" Width="1024"
        WindowStartupLocation="CenterScreen">
    <ListView Name="listView">
        <ListView.View>
            <GridView>
                <GridView.Columns>
                    <GridViewColumn Header="新闻列表">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <TextBlock Width="960">
                                    <Hyperlink NavigateUri="{Binding Url}"
                                               ToolTip="{Binding Summary}"
                                               RequestNavigate="Hyperlink_OnRequestNavigate">
                                        <TextBlock FontSize="20" Text="{Binding Text}" />
                                    </Hyperlink>
                                </TextBlock>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                </GridView.Columns>
            </GridView>
        </ListView.View>
    </ListView>
</Window>
复制代码

 

事件处理程序Hyperlink_OnRequestNavigate的代码如下,启用新进程使用默认浏览器来打开网站(如果不加那个参数,那么总是用IE打开网站):

 

复制代码
private void Hyperlink_OnRequestNavigate(object sender, RequestNavigateEventArgs e)
{
    (sender as Hyperlink).Foreground = Brushes.Red;
    var uri = e.Uri.AbsoluteUri;
    Process.Start(new ProcessStartInfo(WindowsHelper.GetDefaultBrowser(), uri));
    e.Handled = true;
}
复制代码

 

WindowsHelper类的代码:

 

复制代码
public static class WindowsHelper
{
    private static string defaultBrowser;
    public static string GetDefaultBrowser()
    {
        if (defaultBrowser == null)
        {
            var key = Registry.ClassesRoot.OpenSubKey(@"http\shell\open\command\");
            var s = key.GetValue("").ToString();
            defaultBrowser = new string(s.SkipWhile(c => c != '"').Skip(1).TakeWhile(c => c != '"').ToArray()).Trim().Trim('"');
        }
        return defaultBrowser;
    }
}
复制代码

 

7、使用.NET 4.5的异步特性来处理多个网站的加载问题

程序会自动记录浏览记录,且已浏览的链接不再显示出来。这里比较耗时的功能有:从xml文件中反序列化出历史数据、从各个网站下载并解析,它们是可以并行的,然而解析完成之后要排除历史数据中已有的数据,这个过程需要等待反序列化过程完成,代码如下:

 

复制代码
deserialization = new Task(delegate
                            {
                                try
                                {
                                    history = NEWSHISTORY_XML.Deserialize<List<HistoryItem>>();
                                    history.RemoveAll(h => h.Time < DateTime.Now.AddDays(-7).ToInt32());
                                }
                                catch (Exception)
                                {
                                    history = new List<HistoryItem>();
                                }
                            });
cnblogs = new Task(async delegate
                            {
                                try
                                {
                                    var result = Cnblogs();
                                    await deserialization;
                                    AddIfNotClicked(result);
                                }
                                catch (Exception exception)
                                {
                                    itemsSource.Add(new ShowItem
                                                    {
                                                        Text = "Cnblogs Fails",
                                                        Summary = exception.Message
                                                    });
                                }
                                listView.Dispatcher.Invoke(() => listView.Items.Refresh());
                            });
cnbeta = new Task(async delegate
                        {
                            try
                            {
                                var result = CnBeta();
                                await deserialization;
                                AddIfNotClicked(result);
                            }
                            catch (Exception exception)
                            {
                                itemsSource.Add(new ShowItem
                                                {
                                                    Text = "CnBeta Fails",
                                                    Summary = exception.Message
                                                });
                            }
                            listView.Dispatcher.Invoke(() => listView.Items.Refresh());
                        });
deserialization.Start();
cnblogs.Start();
cnbeta.Start();
复制代码

 

复制代码
private void AddIfNotClicked(IEnumerable<ShowItem> result)
{
    foreach (var item in result.Where(i => history.All(h => h.Url != i.Url)))
    {
        itemsSource.Add(item);
    }
}
复制代码

 

itemsSource = new List<ShowItem>();
listView.ItemsSource = itemsSource;

 

8、结语

以上就是给自己经常访问的网站做信息抓取的实践了,实际上做出的东西对我来说是很有用的,我再也不会像以前那样,隔一会儿就要打开网站看追的美剧有没有更新了。对博客按推荐数排序,是一种比较高效的方式了。

由于代码中有我的Cookie,就不放出下载了。

应要求,给个demo,我把需要登录的哪些网站去掉了,保留了一个福利网站。

[转载]抛弃NVelocity,来玩玩Razor - 一线码农 - 博客园

mikel阅读(1194)

[转载]抛弃NVelocity,来玩玩Razor – 一线码农 – 博客园.

对于内容型,不易变动的东西我们都希望给它来个静态化,还有种情况就是比如新浪云不支持.net,为了能跑起我们的网站

只能放些静态页面上面,外加jsonp来实现交互,我们知道.net中有很多模板引擎,但都不是原装的,所以基本都没有代码提示,

用起来比较不爽,自razor出来后,私活中也抛弃了原先使用的NVelocity,而采用原装的razor。

好了,我们先看看怎么玩,首先我们去codeplex上下两个dll。http://razorengine.codeplex.com/

我想razor语法,大家都已经滚瓜烂熟了,不过我快一年都没接触mvc了,也忘了差不多了,就简单介绍一些吧~~~

一般来说,只要razor支持四样特性,80%的功能都可以完成。

 

一:简单的占位符替换

由于太简单了,就不花力气从cshtml文件中读取了,而用字符串替代。

怎么样,简单的占位符替换是不是很简单,比那些{0},{1}是不是更有语意化一些~

 

二:能实现的if,foreach等流程控制语句

先看看在模板中怎么玩,最最关键的是有代码提示,完完全全是我们熟悉的C#语法

复制代码
 1 <!DOCTYPE html>
 2 
 3 <html lang="en">
 4 
 5 <head>
 6     <meta charset="utf-8" />
 7     @* 看看if语句怎么玩 *@
 8     @if (DateTime.Now.Hour == 23)
 9     {
10         <title>你好,现在是午夜</title>
11     }
12     else
13     {
14         <title>现在时间是:@DateTime.Now.ToString()</title>
15     }
16 </head>
17 
18 <body>
19     <ul>
20         @* 看看foreach语句怎么玩 *@
21         @foreach (var item in @Model.StudentList)
22         {
23             <li>@item.Name</li>
24         }
25     </ul>
26 </body>
27 </html>
复制代码

再看看后台C#代码

复制代码
 1   class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             string path1 = Environment.CurrentDirectory + "../../../index.cshtml";
 6 
 7             var index = System.IO.File.ReadAllText(path1, Encoding.GetEncoding("GBK"));
 8 
 9             var list = new List<Student>()
10             {
11                  new Student(){ Name="张三" },
12                  new Student(){ Name="李四" },
13                  new Student(){ Name="王五" },
14             };
15 
16             var result = Razor.Parse(index, new { StudentList = list });
17 
18         }
19     }
20 
21     public class Student
22     {
23         public string Name { get; set; }
24     }
复制代码

然后我们看看生成的文本,嘿嘿,是不是找到了写mvc的感觉呢

 

三:添加自定义方法

在静态化中,添加自定义方法也是非常实用的一项功能,razor实现起来也很简单,我们可以重写原有的TemplateBase类

来实现我们自定义的类,然后加上一些我们自定义的方法即可~

复制代码
 1     /// <summary>
 2     /// 自定义razor原有模板,增加一些自定义的全局方法
 3     /// </summary>
 4     /// <typeparam name="T"></typeparam>
 5     public abstract class MyCustomTemplateBase<T> : TemplateBase<T>
 6     {
 7         public string MyUpper(string name)
 8         {
 9             return name.ToUpper();
10         }
11     }
复制代码

这里实现个简单的“大写化”方法,很简单,然后使用  Razor.SetTemplateBase(typeof(MyCustomTemplateBase<>))进行设置。

复制代码
 1         static void Main(string[] args)
 2         {
 3             Razor.SetTemplateBase(typeof(MyCustomTemplateBase<>));
 4 
 5             string index_path = Environment.CurrentDirectory + "../../../index.cshtml";
 6 
 7             //index内容
 8             var index = System.IO.File.ReadAllText(index_path, Encoding.GetEncoding("GBK"));
 9 
10             var result = Razor.Parse(index, new { Name = "ctrip" });
11         }
复制代码

再看看我们前端的index.cshtml中定义

复制代码
 1 <!DOCTYPE html>
 2 
 3 <html lang="en">
 4 
 5 <head>
 6     <meta charset="utf-8" />
 7     <title>hello</title>
 8 </head>
 9 
10 <body>
11     @MyUpper(Model.Name)
12 </body>
13 </html>
复制代码

大功搞成,看看效果。

 

四:文件嵌套

我们知道页面一般都有公共的东西,比如头部,尾部,导航等等,只可惜的是在razor中是不支持layout的,而是使用razor

给我们提供的include加上后台的Compile预编译方法。

第一步:定义一个header.cshtml模板文件

 

第二步:定义个index.cshtml模板文件,然后在heder区域中用include定义起来,第一个参数Header是给Header.cshtml

的命名,Model是作为Header.cshtml中的实体参数。

 

 

第三步: 分别读取header.cshtml和index.cshtml中的内容,将前者编译成一个变量Header。

复制代码
 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             //header文件的路径
 6             string header_path = Environment.CurrentDirectory + "../../../header.cshtml";
 7 
 8             //index文件的路径
 9             string index_path = Environment.CurrentDirectory + "../../../index.cshtml";
10 
11             //header内容
12             var header = System.IO.File.ReadAllText(header_path, Encoding.GetEncoding("GBK"));
13 
14             //index内容
15             var index = System.IO.File.ReadAllText(index_path, Encoding.GetEncoding("GBK"));
16 
17             //将header.cshtml内容预变成为header变量,对应到index.cshtml中的include
18             Razor.Compile(header, "Header");
19 
20             var list = new List<Student>()
21             {
22                  new Student(){ Name="张三" },
23                  new Student(){ Name="李四" },
24                  new Student(){ Name="王五" },
25             };
26 
27             var result = Razor.Parse(index, new { Name = "我的测试页面", StudentList = list });
28 
29         }
30     }
31 
32     public class Student
33     {
34         public string Name { get; set; }
35     }
复制代码

 

好了,如果掌握了上面四样基本功能,现在你也可以用razor实战到自己的静态化中去啦o(∩_∩)o

[转载]那些年一起用过的iOS开发利器之Parse - LloydSheng - 博客园

mikel阅读(1180)

[转载]那些年一起用过的iOS开发利器之Parse – LloydSheng – 博客园.

阅读此文章需要对Objective-C和iOS有一定的了解,完全没有基础的朋友请先阅读《让不懂编程的人爱上iPhone开发》系列教程。

什么是后台服务(back-end service)?说白了,就是联机服务。比如说一个备忘录应用,如果所有的数据信息只能保存在本地设备(也就是你所用的电脑、手机、平板电脑上),那么 它就是一个单机应用。如果一个游戏只能在自己的设备上玩,而且所有的游戏进度、档案都保存在本地设备上,那么它就是一个单机游戏。

因此,简单来说,后台服务的作用就是把单机应用变成网络应用,把单机游戏变成网络游戏。

现在如果说哪一个移动应用(非游戏)没有提供后台服务的功能,几乎就是不可想象的。不过之前为了提供所谓的后台服务,你需要拥有自己的数据库和 web服务器,这个很少是免费的。除非之外,你还需要借助于类似Ruby on Rails,PHP或JAVA等编程语言来实现后台服务。但这样做有几个问题:

  1. 大量时间的投入:

为移动应用提供后台服务需要大量的开发时间和精力的投入。通常来说,开发后台服务的时间和客户端的时间几乎是一样的。

  1. 更高的技能要求:

即便你已经是iOS或Android客户端开发高手,也不意味着你在后台服务的开发上同样NB。事实上,绝大多数程序猿都是专攻其中一项,能够前端后端同时开火左右手互搏的牛人当然有,只是要耗费大量的时间沉淀才可能达到这一步。

  1. 用户规模可扩展性的问题:

移动应用的天性决定,你永远不知道你的应用只是小众俱乐部成员,还是哪天成为百万用户俱乐部的一员。除非是后台开发的高手,很难在开发应用时让应用具有极好的可扩展性。

4.金钱的投入

即便是选择最便宜的虚拟主机服务,你也得为此支付一定的费用。

正是因为有以上问题的存在,近年来出现了不少专门针对移动应用/游戏的第三方后台服务。

 

PART I 关于BAAS服务

随着移动应用开发市场的火爆,已经有聪明人认识到了开发者的这一强烈需求,并提供高度可定制的网络后台服务模块,方便开发者整合到自己的应用之中。 有砖家给这种服务提供了一个新的名词,美其名曰Backend as a Service(后台服务),或者更NB的说法叫BaaS。 这些后台服务提供商通常会提供一个整合的方案,其中包括了后台数据库存储,以及其它 可定制化的服务。而在iOS和Android应用中整合这些服务并不难,只需要调用一两个类库就好。大多数此类服务可以提供免费账号,当然也针对规模可扩 展性问题提供了不同的付费账号标准。

让我们大概了解下BaaS提供商所提供的基本功能。 这样吧,让我们假想一个应用,比如我们要存储属于不同队伍的运动员。这些BaaS提供 商能帮我们些什么呢?
1. Custom objects(可定制的对象):通常来说,后台服务的最重要功能就是可以将 应用数据存储到网络服务器的数据库中。在你的应用中一般都有代表数据的model对象(比如一个运动员对象,以及一个球队对象),以及对象间的关系(比如 某个运动员属于哪一只球队)。BaaS服务提供商通常会让开发者用一种简单的方式从对象中获取数据, 并将其存储/保持到网络服务器的数据库中。

2. File storage(文件存储):除了保存数据库类型的数据,有时开发者还需要保存文 件-比如图片,大型文档等等。在这款假想的应用中,或许你需要为每一个运动员保存一张肖像照片。绝大多数的后台服务都提供文件存储功能,方便开发者将文件 和某个定制对 象关联在一起。

3. Geolocation:(地理位置信息)在大多数的移动应用中,可能会需要根据某个特定的位置来查询对象。例如,用户可能想查找自己手机附近的球队。BaaS服务允许开发者将 自己的对象标记一个特定的地理位置,这样用户就可以根据地理位置来查找对象。
4. Users(用户):很显然开发者需要对应用中的对象实行权限管理,并指定用户可以访问的特定对象。几乎所有的BaaS服务都允许开发者创建自己的用户, 而大多数用户都 通过Facebook或Twitter来登录。这样既可以扩大用户基数,同时也无需让用户创建新的 账户。

5. Push notification(推送服务):BaaS服务允许开发者向自己的所有或选定用户发送 推送消息。 好吧,这样一来,你大概就清楚了BaaS服务提供商能帮到你些什么。接着让我们来大概 了解下几个主要的BaaS服务提供商吧。

1. StackMob(StackMob)

可能是目前所有BaaS服务中最流行也是最具可定制性的服务了。开发者可以上传自己的后端代码,创建自己的网络服务,这一点让它在BaaS服务提供商中显得鹤立鸡群。

当然也有不爽的地方,如果你要存储二进制文件(比如假想应用中的球员肖像图片),还得需要一个Amazon S3账户。开发者无法直接在StackMob中维护管理二进制文件。他们的免费计划也稍显吝啬,但付费服务计划就显得非常慷慨合理了,而且扩容方面很方便。

2. Appcelerator Cloud Service (Cloud | Titanium Mobile App Development ) 毫无疑问,该服务是所有BaaS服务中功能最完整的,而且免费计划相当慷慨。不过它是设计来和Titanium SDK一起使用的,所有帮助文档稍显晦涩。而如果开发者不是在使用 Titanium的话,会发现有很多看不懂的地方需要跳过。 当然,该网站也提供了很不错的Q&A⻚页面帮助开发者解决各类问题,而且对问题的响应 非常迅速。

3. Parse(Parse):

提供所有BaaS服务中最好的免费服务计划。其帮助文档也是最容易理解的,所提供的服 务特别适合初学者,可以很短时间里轻松上手(译者推荐)。 Parse还提供了示例项目,以及可以在项目中添加的定制UIView视图,比如Facebook和 Twitter登陆视图。此外,它还提供了大量可用的第三方类库。

4. Applicasa(Virtual Store Platform, Complete In-App Purchase & Backend Solution): 提供了和其它BaaS相似的功能,不过增加了一个拖曳功能,可以方便开发者创建自己的 表格和定制化对象。一旦数据库结果设计完成,就可以下载定制的SDK。Applicasa的收 费基于你的活跃用户数量。如果你是创业型团队,那么它会提供一个免费账户,允许每月 10万人的活跃用户。不过你得主动联系他们申请。

5. Kinvey(Backend as a Service): 同样是一个很流行的BaaS服务。开发者在转为付费账户前,该服务支持200个活跃用户。不过它的付费价格还是比较高的。当然,大浪淘沙唯留真金,在很短 的时间里面Parse已经成为了最主流的BAAS服务提供 商之一,特别是被Facebook以8500万美刀的价格收购之后(参考新闻http:// tech.sina.com.cn/i/2013-04-26/11458285564.shtml),Parse更是成为了BAAS服务的首 选。为了证明Parse可以为移动应用开发节约后台相关的时间,Parse提供了一个仿 Instagram的应用,号称在30分钟内即可开发完成(36氪 | 关注互联网创业139629.html)。

PART II Parse简介

在上面的内容中我们已经了解到Parse也是BAAS服务的一种,那么它有什么样的特点 呢? parse的最大特定是简单易用,提供完美的文档支持,可以在极短的时间内上手。

1. Parse Data

类似网络数据库的功能,可以在云端存储应用中的数据,无需配置自己的服务器。 支持多种数据类型:

可存储的数据类型包括基本数据类型,地理位置信息,照片和查询等等,可完全替代 MySQL和NoSQL数据库。
云端更新:
Parse Data具有云端更新功能,开发者可以通过这里上传应用中的新资源文件,并立即 推送到应用中,而无需经过苹果的第三方审查重新提交应用。

强大的数据管理功能: 支持可视化操作,可以在无需编写一行代码的情况下管理,查询和更新服务器数据。 Parse提供了Data browser数据浏览器,允许开发者在类似excel表格的界面中管理数据库 中的数据。 当然,如果数据库中的数据很多,parse还提供了强大的filtering筛选功能,通过设置筛选 条件(类似sql语句),可以迅速找到需要更新的数据。
在data browser中开发者还可以直接添加或删除数据列,删除所有数据,设置访问权限 等。 此外,如果开发者需要一些定制功能,还可以直接联系parse(Plans | Parsecontact_sales)。

2. Parse Push(推送服务)
使用Parse Push可以创建、预设置和分割推送消息。parse的推送服务SDK简单高效,可 以通过网⻚页平台,REST API或客户端SDK来发送推送消息。
Parse Push的推送服务支持超大规模推送,无论你的应用有多少需求,都可以扩展满 足。
可 视化的Push Composer:通过Parse dashboard里面的Push Composer,可以很方便的 创建、预览和发送推送消息到所有平台(iOS,Android,win8)。其简单程度和发送邮件无 异。 开发者只需要在文本框中输入相关信息,设置要发送的时间,格式(文本或JSON),并 选择要推送的设备类型(可全选),就可以立即在设备上看到效果。 精准目标设定:Pro和企业用户还可以设置特定的条件将消息推送给特定用户。

3. Parse Social

只需短短几行代码,就可以创建自己的账号系统,或者和第三方社交网络绑定 (twitter,facebook)。parse social负责账号绑定,密码设置,信息验证和安全等最繁琐 的事情。当然,类似微博微信这样的天朝社交网络还是需要自行绑定的。

此外,parse social允许用户在多个不同设备上使用同一账号来登录。 4. Parse Hosting-为网站所推出的特别服务

类似于云存储的服务,利用支持JavaScript SDK和Cloud Code来实现。 让你的网站为他人所知,parse hosting可以保持静态网⻚页HTML,JavaScript,CSS,并通 过和Cloud Code交互创建动态⻚页面,从而无需后台服务器。
如果升级到Parse Pro,还可以支持自定义域名。
Parse Hosting的价格如下:
(1)免费服务: 每月100万次API请求,每超出1000次收费7美分,文件存储1GB免费空间,每超出1GB 收费20美分
(2)付费199美元服务: 每月1500万次API请求,每超出1000次收费5美分,文件存储10GB,每超出1GB收费15 美分
(3)企业级服务:
可以联系对方确认。

可以看到,Parse Hosting 的服务价格从文件存储的角度来看并没有太大优势,对于重度 文件存储类应用需要考虑其它云服务。此类用户可以考虑StackMob +Amazon S3服务。

5.Cloud Code 支持iOS,Android或HTML5,

6.所支持平台或服务:
iOS, OSX,ANDROID,JavaScript,Windows Phone 8,Windows 8,.NET,RESTful API

parse的整体服务价格是基于API调用的:
1. 免费: 支持每月 100万 次API调用, 支持每月 100万次推送服务,峰值每秒20次

2. 付费:每月199美刀,支持每月1500万次API调用,支持每月500万次推送服务,峰

值每秒40次,提供30天的免费试用期

3. 企业级服务:可以单独谈。

PART III. Parse的iOS应用示例(使用Xcode4.6, iOS6.1)

该部分内容涉及到代码(示例代码参考了parse官方示例及raywenderlich网站中的相关教程),详细内容参考链接中的pdf文件:

http://vdisk.weibo.com/s/3Q0p9MS10TK

原文: http://zhuanlan.zhihu.com/kidscoding/19597061

—-文章完—-

最后,推荐一个神器

内测宝

个人觉得比TestFlight更简单好用,开发者只需要简单把打好的ipa包上传上去,生成二维码,测试人员在手机上扫码二维码,就可以直接安装最新的测试版本了,好用的让人想哭。

[转载]碉堡撸一撸,科技与淫荡的十字路口 | 雷锋网

mikel阅读(793)

[转载]碉堡撸一撸,科技与淫荡的十字路口 | 雷锋网.

夜深了。

夜里很安静,在这样寂寞的夜里,我感觉整个人似乎跟夜融为了一体。我个人就应该这么安静,我该属于这份黑暗。

品味孤单,这不是每一个人都能做得到。我陷入一种迷离的状态,周边似乎从强互作用力般凝固安静的状态中,慢慢地开始有一些细细的波动,似乎是一些微波的外扩,空气中有了一点颤动,有了一丝味道。

我拿起手机,打开碉堡。

今夜,注定碉堡。

1

漆黑的夜里,整个空间里满是土豪金屏幕发出的亮光。而更重要的,似乎是这片亮光之中出现的碉堡。碉堡自诞生以来,从来不让我失望。那些年,每当碉民早爆在碉堡微信号发出,我就会扑上去打开阅读 – 因为,不用多久,它就会意不反顾地显示被举报无法阅读。

如今,碉堡刚刚改版,但即便从打开的首页,我就可以看到,碉堡仍不让人失望。尽管顶部的各种点是简化了一些,目前只剩下首页推荐、爆点、尿点和撸点了。但栏目的多寡,从来不是影响一个APP内容精彩与否的因素。

这一瞬间,如此安静的夜。

我站在了,科技与淫荡的十字路口。

2

所以,当我仍很容易地找到这两个地方,两个我称之为精神归属的地方。他们一个叫碉民早爆,一个叫每日一撸。我知道,碉堡,仍是那个碉堡。

漆黑的夜里,土豪金的亮光中,我的思绪飘离很远,回到14岁那年夏天,我穿着裤衩躺在工地的床上。我搬完砖在午休,在一张简陋的木板床上,在这个特殊的下午,我开启了身上的一个开关,像是打通了任督二脉,走进了一个新的天地。

3

夜,静谧。我静静地在碉民早爆和每日一撸之间滑翔,心也在飞翔。只是,不小心手一滑,竟至打开了底栏的兴趣一栏。记得改版之前,这里是叫订阅的存在,里面全是自媒体账号,当然以大多无趣的科技类为主。

这次手滑打开之后,发现内容线索改为了用户兴趣,自媒体版块相当于被降了地位。

干得漂亮。

很快,我点开了比基尼。

4

其实,你最终还是得到这里来。这似乎是一种特定的规律,一种真诚的向往,一种虔诚的朝拜。即便他人告诉你应该一路向西,你却仍然会绕到这里。

这是这次碉堡更新最重要的功能,撸一撸。而它,就静静躺在广场宽大简洁的空间里,等你开撸。

5

它不像摇一摇,摇了之后可能会发生很多事,但后来发生的事儿,基本跟摇这个动作没什么关系了。

但撸一撸会告诉你,你必须真的得“上下上下撸,而不是摇一摇”。

更重要的是,你在手机撸完之后,后来发生的事儿,跟你对手机做的动作,是一致的。

它是真诚的。

即便,它会在让你给妹纸打分的时候卖个萌。

6

我给妹妹打个分,不带走一丝云彩。我打开排行榜看看,无任何的感觉与反应。

当然,说上面这句话时,我的右手按在左边胸前。

7

在这深深的寂寞的夜,骚动渐起,气喘渐浓,频率走高,一阵震颤,重归于寂。

知我者碉堡。

iOS       Android

[转载]开通微信支付的方法【图文教程】 - 站长圈

mikel阅读(1104)

[转载]开通微信支付的方法【图文教程】 – 站长圈.

申请条件/申请资格(来自微信)

申请成为公众账号支付商户需要满足,必须满足以下条件:

1、拥有公众帐号,且为服务号;

2、公众帐号必须经过微信认证,且为企业、商店商家、媒体认证类型(未认证用户,可先申请微信认证);

申请成为APP支付商户需要满足,必须满足以下条件:

1、拥有公众帐号,且为服务号;

2、公众帐号必须经过微信认证,且为企业、商店商家、媒体认证类型(未认证用户,可先申请微信认证);

3、APP支付商户,须拥有微信开放平台(http://open.weixin.qq.com)的APPID。

登录微信公众平台,点击“服务”功能栏下的“服务中心”模块,再点击右侧的“商户功能”模块。

在弹出的对话框中,此时需填写“商户基本资料”、“业务审核资料”、“财务审核资料”等资料。

商户基本资料填写一:选择最上面一项,首先填写商户基本资料,申请所需资料以及步骤提示。

商户基本资料填写二:如果上述资料您都准备齐全了,那就点击》=准备完毕,开始填写。进入下级菜单,开始填写商户基本信息。

业务审核资料填写一:在填写好商户基本资料后,返回主菜单,填写业务审核资料,申请所需资料以及步骤提示。

业务审核资料填写二:根据提示步骤,提前准备好资料,点击》=准备完毕,填写资料。这里需要填写,商户负责人信息和商户基本信息。

业务审核资料填写三:填写好上面的资料后,进入下一步,在弹出的下级菜单中,填写商户资质信息!
注意事项:
1、商户资料需要与认证的商户主体一致,即认证主体与运营主体一致(填写的信息需与申请微信认证的信息一致)。
2、切记此栏中填写的“重要邮箱”,该邮箱为接收微信支付相关重要信息。

财务审核资料一:在填写好业务审核资料后,返回主菜单,填写财务审核资料,申请所需资料以及步骤提示。

财务审核资料二:根据提示步骤,提前准备好资料,点击》=准备完毕,填写资料。这里需要填写,企业对公帐号和加盖公司公章的证明函。

[转载]iOS开发之通知中心(NSNotificationCenter) - 求真求道 - 博客园

mikel阅读(1019)

[转载]iOS开发之通知中心(NSNotificationCenter) – 求真求道 – 博客园.

                                                                                        前 言                                                                                              

面向对象的设计思想是把行为方法封装到每一个对象中,以用来增加代码的复用性。正是这种分散封装,增加了对象之间的相互关联,总是有很多的对象需要彼此了解以及相互操作!

一个简单示例说明这种交互产生的对象之间的相互依赖:

有一个列表框,每当列表框的某一项被选中时,就会弹出一个对话框,对话框包含一个或多个输入框,在输入框输入完毕 输入的文字要更新要列表中显示。文字描述有点啰嗦,大家可以想一下QQ的好友列表,这与其类似的!

只是这几个UI控件之间的交互逻辑就如此的麻烦与复杂。这种对象之间的相互关联与我们的高复用,低耦合思想是不符合的。并且UI控件的增加,UI控件之间势必需要相互了解及操作,情况就会变得越来越难以控制,最终整个程序变得难以复用与维护!

                                                                     通知中心 NSNotificationCenter                                                                             

iOS开发中的通知中心(NSNotificationCenter)机制解决以上的问题!在其他的开发应该也存在相类似的处理机制,这种处理机制的设计模式被称为中介者模式!

每一个应用程序都有一个通知中心(NSNotificationCenter)实例,专门负责协助不同对象之间的消息通信。

任何一个对象都可以向通知中心发布通知(NSNotification),描述自己在做什么。其他感兴趣的对象(Observer)可以申请在某个特定通知发布时(或在某个特定的对象发布通知时)收到这个通知!

如下图:

360软件小助手截图20140403013523

 

一个对象要想接收到通知,必须先在通知中心进行注册。这一点与我们生活中的新闻订阅是相同的!

                                                                             注册通知监听 器                                                                                             

通知中心(NSNotificationCenter)提供了方法来注册一个监听通知的监听器(Observer)

- (void)addObserver:(id)observer selector:(SEL)aSelector name:(NSString *)aName object:(id)anObject;
//observer:监听器,即谁要接收这个通知
//aSelector:收到通知后,回调监听器的这个方法,并且把通知对象当做参数传入
//aName:通知的名称。如果为nil,那么无论通知的名称是什么,监听器都能收到这个通知
//anObject:通知发布者。如果为anObject和aName都为nil,监听器都收到所有的通知

 

- (id)addObserverForName:(NSString *)name object:(id)obj queue:(NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block;
//name:通知的名称
//obj:通知发布者
//block:收到对应的通知时,会回调这个
//blockqueue:决定了block在哪个操作队列中执行,如果传nil,默认在当前操作队列中同步执行

                                                                                       通 知

一个完整的通知一般包含3个属性:
– (NSString *)name; // 通知的名称
– (id)object; // 通知发布者(是谁要发布通知)
– (NSDictionary *)userInfo; // 一些额外的信息(通知发布者传递给通知接收者的信息内容)

                                                                    初始化一个通知(NSNotification)对 象

 

+ (instancetype)notificationWithName:(NSString *)aName object:(id)anObject;
   + (instancetype)notificationWithName:(NSString *)aName object:(id)anObject userInfo:(NSDictionary *)aUserInfo;
   - (instancetype)initWithName:(NSString *)name object:(id)object userInfo:(NSDictionary *)userInfo;

                                                                                      发布通 知

 

通知中心(NSNotificationCenter)提供了相应的方法来帮助发布通知

发布一个notification通知,可在notification对象中设置通知的名称、通知发布者、额外信息等

- (void)postNotification:(NSNotification *)notification;

 

发布一个名称为aName的通知,anObject为这个通知的发布者

- (void)postNotificationName:(NSString *)aName object:(id)anObject;

发布一个名称为aName的通知,anObject为这个通知的发布者,aUserInfo为额外信息

- (void)postNotificationName:(NSString *)aName object:(id)anObject userInfo:(NSDictionary *)aUserInfo;

[任务]¥6000 产品品牌网站建设

mikel阅读(1003)

任务地址:http://task.zhubajie.com/3913663/

任务要求:

需求号:3913663

具体要求:

网站用途:1.推广公司产品品牌;2.建立与客户沟通的网络渠道;3.产品展示
开发语言:php+html+css
参考网站:无
具体要求:
1、不使用建站工具,全新开发,包括前端页面设计及相关功能开发(注:功能并不复杂,且需求明确);
2、页面结构简洁,静态网页为主,配合部分动态功能模块;
3、功能模块:
前台功能:会员系统、互动留言系统;产品定制系统;
后台功能:会员管理系统、邮件推送系统;
4、其他要求:
必须在武汉本地,有充足时间当面沟通,能够同时完成前、后台设计;

[转载]12306购票助手开源啦 - 飞无痕落无声 - 博客园

mikel阅读(1747)

[转载]12306购票助手开源啦 – 飞无痕落无声 – 博客园.

去年春运期间写了一篇文章《火车票抢票攻略》得到很多园友们的支持,今天我把源代码放在github上了,有兴趣的同学可以去看看。

源码地址:https://github.com/lq1016/Ticket12306

waner8.common是我自己的一个工具类,AutoUpdater是自动更新的库,Ticket12306是客户端代码。

由于春运期间时间紧,再加上自己能力有限,代码难免简陋,更或许漏洞百出,请大家多多支持和提建议,谢谢。

[转载]工欲善其事必先利其器---SQL在线可视化模型设计,(还可学习拖拽知识) - 沫尘 - 博客园

mikel阅读(1085)

[转载]工欲善其事必先利其器—SQL在线可视化模型设计,(还可学习拖拽知识) – 沫尘 – 博客园.

作为技术人员,在开发项目中,不可避免的要跟数据库打交道,一个完整的项目正常情况下是讨论完整体需求,有了大致的框框在脑海中后,是需要设计合理的数据 库的,这时会有其他的专业的UML建模工具可以使用, 但是这种大型的专业化的UML建模工具,仅仅拿来建立数据库模型显得大材小用了,并且也太重量级,所以一般都不会轻易使用。多数人会选择直接在数据库软件 中直接鼠标操作建表、填充列, 这样更是不方便,  故而很多人提到了VS中的codefirst方式,  直接在代码中建立数据库模型,  这种方式生成数据库之后,如果回头维护、查看都不太友好,甚至过了几个月之后你自己回头看都有可能不知道是什么意思。  这样我们就需要数据库说明。

常见的数据库说明都是word文档,类似如下:

这是很多公司或团队采用的, 但是我觉得这样还是不爽,假定你是一个接手人, 你拿着这样的表格,左边点开数据库,右边打开这个文档,瞄一眼数据库再瞄一眼这个表格,左右切换窗口,超级不爽啊。

另外一种更加直观的,正向思维的方式:

 

这样看起来是不是直观多了,  清楚明白的知道这个表的构成,直接在数据库中打开,不用切换窗口,它是建表脚本,也是一份详细直观的数据库说明文档, 后续对数据库进行修改、增加、删除结构都很直接、直观。

于是,我个人就弄了个自己用的方便的可视化的数据库设计的东西,当然最终是生成上述直观可读性高的脚本文件,今天把它写出来分享下。

我的环境是mvc3,  其实这个东西根本用不着MVC,  很简单,就一个页面大量的JS操作,提交到后台代码生成文件下载回本地。先看打开时:

默认加载一个空表,填写相应的名称之类的,字段、类型、默认值、备注信息等,+号增加一行,如果一行没有完成会提示:

点击新建表会增加一个表,整个筐筐内每个表模型可以任意拖动位置且高度会自适应。拖拽都是靠JS。整个界面代码如下:

@{
ViewBag.Title = "主页";
}
<div class="box">
<div class="editmenu">

<input id="txtBaseName" type="text" value="@ViewBag.baseName" />

<input id="txtBaseDes" type="text" value="@ViewBag.baseDes" />
<input id="addTable" type="button" value="新建表" />
<input id="getData" style="float: right;" title="下载完整的TXT格式的数据库脚本" type="button" value="下载数据" />

</div>
<div class="main" id="main">
<div class="tbbox">
<div class="titletr" title=""><span class="spleft">
<input class="tbname" type="text" value="XXX表" />
</span>
</div>
<div class="titletr" title=""><span class="spleft">
名:<input class="tbname" type="text" value="tb_1" />
</span>

<span class="spright">删除</span></div>
<table class="tablelist">
<tbody>
<tr>
<td>字段名</td>
<td>类型</td>
<td>默认值</td>
<td>备注</td>
<td style="border: 0;"></td>
</tr>
<tr>
<td><input type="text" /></td>
<td>
<div class="sortdiv"><span class="selspan"><span class="selspan"><select class="selsort"><option value="int">int</option><option value="varchar(50)">varchar(50)</option><option value="nvarchar(200)">nvarchar(200)</option><option value="datetime">datetime</option><option value="float">float</option><option value="text">text</option><option value="ntext">ntext</option></select></span></span>&nbsp;
<input class="sortinput" type="text" value="int" /></div></td>
<td><input class="txtdef" type="text" /></td>
<td><input type="text" /></td>
<td class="jiatr" id="tool" style="border: 0;">+</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<input id="hdbase" type="hidden" value="@ViewBag.baseId" />

<input id="hdtr" type="hidden" value="@ViewBag.Tr" />
<input id="hddiv" type="hidden" value="@ViewBag.div" />
<input id="hdall" type="hidden" value="@ViewBag.AllData" />

<script type="text/javascript">// <![CDATA[
        $(document).ready(function () {
            window.onload = function () {
                if ($("#hdall").val() != "") {
                    $("#main").html($("#hdall").val());
                }
                ///初始化给div加移动事件
                $("#main").find("span.title").each(function () {
                    this.onmousedown = MoveBegin;
                });
                ///给+TD事件
                $("#main").find("td.jiatr").each(function () {
                    $(this).click(TdClick);
                    $(this).attr("title", "新增一行");
                });
                ///添加DIV按钮
                $("#addTable").click(AddTbale);
                $("#getData").click(GetData);
                $("#saveData").click(SaveData);
                ///给select绑定事件
                $("#main").find("div.tbbox").each(function () {
                    SortList(this);
                    $(this).find("span.spright").click(DelDiv);
                });

                CheckMainHeight();

            }
            ///返回txt文件
            function SaveTxt(arr) {
                var loogParam = arr;
                var nwin = window.open('/home/DataText?t=' + Math.random(), 'newwindow', 'height=5, width=5, top=400,left=500, toolbar=no, menubar=no, scrollbars=no, resizable=no,location=no, status=no');
                document.body.insertAdjacentHTML("beforeEnd", "
<form  name='form1'  action='/home/DataText'  target='newpage'  method='post'><input  type='hidden'  name='param'></form>

");
                document.form1.param.value = loogParam;
                document.form1.submit();
                $("form").remove();
                nwin.close();
            }

            ///txt文件空格占位
            function NullSpace(defnum, str) {
                var s = "\040";
                var r = "";
                var l = str.length;
                var num = defnum - l;
                for (var i = 0; i < num; i++) {
                    r += s;
                }
                return r;
            }
            ///"-"占位符
            function NullH(defnum, str) {
                var s = "-";
                var r = "";
                var l = str.length;
                var num = defnum - l;
                for (var i = 0; i < num; i++) {                     r += s;                 }                 return r;             }                          ///下载脚本为txt文件             function GetData() {                 var tbName = "";                 var tr = 0;                 var sav = true;                 var br = "\r\n";                 var strSql = "use  " + $("#txtBaseName").val() + br;                 $("#main").find("div.tbbox").each(function () {                     tbName = $($(this).find(".tbname")[1]).val();                     var desName = $($(this).find(".tbname")[0]).val();                     var sqlExists = "if exists ( select *  from  sysobjects where name = '" + tbName + "' and xtype='U') " + br + ""                                 + "drop table " + tbName + "";                     strSql += sqlExists + br;                     strSql += "create table  " + tbName + NullH(67, tbName) + desName + br + "(" + br;                     $(this).find("tr").each(function (index, element) {                         if (index > 0) {
                            var tdlist = $(this).find("td");
                            var key = $(tdlist[0]).find("input").val();
                            if ($.trim(key) != "") {
                                if (key.length >= 17) {
                                    alert(key + "超出了15个字符的限制");
                                    //alert(strSql);
                                    sav = false;
                                    return;
                                }
                                key += NullSpace(17, key);
                                var col = $(tdlist[1]).find("input").val();
                                col += NullSpace(17, col);
                                if (index == 1)
                                    col = "int" + NullSpace(17, "int");
                                var def = $(tdlist[2]).find("input").val() == "" ? "default ''" : "default  " + $(tdlist[2]).find("input").val();
                                def += NullSpace(32, def);
                                if (index == 1)
                                    def = "primary key identity(1,1)" + NullSpace(32, "primary key identity(1,1)");
                                var des = "--" + $(tdlist[3]).find("input").val();
                                if (index == 1)
                                    des = "--主键-" + $(tdlist[3]).find("input").val();
                                strSql += "\040\040" + key + col + def + "NOT NULL , " + des + br;

                                tr++;
                            }
                        }
                    });
                    strSql += ");" + br + br + br;
                });
                if (sav && tr > 0) {
                    $("#saveData").click();
                    SaveTxt(strSql);
                }
            }

            ///给数据类型列加事件
            function SortList(obj) {
                $(obj).find("div.sortdiv").each(function () {

                    $(this).find("select:first").change(ChangeVal).change(DefaultTxt);
                    $(this).find("input:first").focus(ShowSel);

                    $(this.parentNode).mouseenter(ShowSel).mouseleave(HideSel);

                    $(this.parentNode.parentNode).find(".txtdef").blur(DefaultTxt);

                });

            }

            ///删除div
            function DelDiv() {
                $(this.parentNode.parentNode).remove();
            }

            ///改变数据类型
            function ChangeVal(obj) {
                $(this.parentNode.parentNode).find(".sortinput").val($(this.parentNode).find(".selsort").val());

            }
            ///默认值
            function DefaultTxt(obj) {
                var txt = $(this.parentNode.parentNode).find(".txtdef:first")[0];
                if (obj.type == "change") {
                    txt = $(this.parentNode.parentNode.parentNode.parentNode).find(".txtdef:first")[0];
                }
                var val = $(txt).val().replace("'", "").replace("'", "");
                $(txt).val(val);
                if ($.trim($(txt).val()) != "" && $.trim($(txt).val().toLowerCase()) != "getdate()" && $.trim($(txt.parentNode.parentNode).find(".sortinput").val()) != "int" && $.trim($(txt.parentNode.parentNode).find(".sortinput").val()) != "float") {

                    $(txt).val("'" + val + "'");
                }
            }
            ///数据类型下拉显示
            function ShowSel(obj) {
                $(this.parentNode).find(".selspan").show();
                $(this.parentNode).find(".sortinput").width("90px");
                $(this.parentNode).find(".sortinput").css("position", "absolute");
            }
            ///数据类型下拉隐藏
            function HideSel(obj) {
                var td = this;
                var o = obj.relatedTarget || obj.toElement;
                if (!o) {
                    return;
                }
                $(td).find(".selspan").hide();
                $(td).find(".sortinput").width("118px");
                $(td).find(".sortinput").css("position", "");
            }

            ///添加DIV
            function AddTbale() {
                var child = document.createElement("div");
                child.className = "tbbox";
                var name = Math.random().toString().substr(2, 3);
                var div = $("#hddiv").val().replace("tb_Name", "tb_" + name);
                $(child).html(div);
                $(child).find("span.title").each(function () {
                    this.onmousedown = MoveBegin;
                });

                $(child).find("span.spright").click(DelDiv);
                $(child).find("td.jiatr").each(function () {
                    $(this).click(TdClick);
                    $(this).attr("title", "新增一行");
                });
                SortList(child);
                $("#main").append(child);
                CheckMainHeight();
            }

            ///TD增加一行点击
            function TdClick(obj) {
                var tooltipem = $(this.parentNode).find("td:first").find("input:first");
                if ($.trim(tooltipem.val()) == "") {
                    $(tooltipem).tooltip({
                        "title": "这里还没填写呢~!",
                        "trigger": "manual",
                        "placement": "right",
                        "delay": 800
                    });
                    $(tooltipem).tooltip("show");

                    $(tooltipem).focus();
                    setTimeout(function () { $(tooltipem).tooltip("hide"); }, 1800)

                    return false;
                }
                $(tooltipem).tooltip("destroy");
                var tr = $("#hdtr").val();
                var child = document.createElement("tr");
                $(child).html(tr);
                $(this.parentNode.parentNode).append(child);
                SortList(child);
                $(child).find("td.jiatr").click(TdClick);

                $(this).removeClass();
                $(this).unbind();
                $(this).html("");
                $(this).attr("title", "");

                CheckMainHeight();
                $($($(this.parentNode.parentNode).find("tr:first").next("tr"))).find("td:last").addClass("prmkey").attr("title", "第一行默认为主键&自增长");
            }
            ///校验div#main高度
            function CheckMainHeight() {
                $("#main").find("div").each(function () {
                    if (this.offsetTop + $(this).height() > $("#main").height())
                        $("#main").height(this.offsetTop + $(this).height() + 10);
                });
            }

            ///移动
            function MoveBegin(obj) {
                var e = obj || window.event,
                x1 = e.clientX;
                y1 = e.clientY;
                var setOn = this.parentNode.parentNode;
                var defLeft = setOn.offsetLeft;
                var defTop = setOn.offsetTop;

                document.onmousemove = function (event) {
                    var e = event || window.event;
                    //
                    x2 = e.clientX;
                    y2 = e.clientY;
                    //
                    x = x2 - x1;
                    y = y2 - y1;
                    var setLeft = defLeft + x;
                    var setTop = defTop + y;
                    setOn.style.position = "absolute";
                    //setOn.style.filter = 'alpha(opacity=80)';
                    //setOn.style.opacity = "0.8";
                    if (setLeft <= 0) {
                        setLeft = 0;
                    }
                    if (setTop <= 0) {                         setTop = 0;                     }                     if (setLeft >= $("#main").width() - $(setOn).width() - 9) {
                        setLeft = $("#main").width() - $(setOn).width() - 14;
                    }
                    if (setTop > $("#main").height() - $(setOn).height() + 10) {

                        $("#main").height(setTop + $(setOn).height() + 100);

                        setTop = $("#main").height() - $(setOn).height();
                    }
                    setOn.style.left = setLeft + 'px';
                    setOn.style.top = setTop + 'px';
                    //$("#showText").text("Top:" + setOn.style.top + "; Left:" + setOn.style.left + "; OFl:" + "");
                }
                document.onmouseup = function () {
                    this.onmousemove = null;
                }
            }

        });   

// ]]></script>

里面有JS拖拽代码, 都很简单, 上面这些是我2年前做的东西,很多命名之类的都不规范,大家关注方法就好。

后台代码:

public ActionResult Index()
{
StringBuilder sb = new StringBuilder();

sb.Append("");
sb.Append("<input type="\&quot;text\&quot;" />");
sb.Append("

");
sb.Append("");
sb.Append("
<div class="\&quot;sortdiv\&quot;">");
sb.Append("<span class="\&quot;selspan\&quot;"><span class="\&quot;selspan\&quot;">");
sb.Append("
<select class="\&quot;selsort\&quot;">");</select>
<select class="\&quot;selsort\&quot;">sb.Append("</select>
<select class="\&quot;selsort\&quot;"><option value="\&quot;int\&quot;">int</option></select></span></span>");
sb.Append("
varchar(50)

");
sb.Append("
nvarchar(200)

");
sb.Append("
datetime

");
sb.Append("
float

");
sb.Append("
text

");
sb.Append("
ntext

");
sb.Append("

");
sb.Append("

");
sb.Append("<input class="\&quot;sortinput\&quot;" type="text" value="\&quot;int\&quot;" />");
sb.Append("</div>
");
sb.Append("

&nbsp;

");
sb.Append("");
sb.Append("<input class="\&quot;txtdef\&quot;" type="\&quot;text\&quot;" />");
sb.Append("

");
sb.Append("");
sb.Append("<input type="\&quot;text\&quot;" />");
sb.Append("

");
sb.Append("
");
sb.Append("+");
sb.Append("

");
ViewBag.Tr = sb.ToString();

StringBuilder sbdiv = new StringBuilder();
sbdiv.Append("
<div class="titletr" title="">");
sbdiv.Append("<span class="spleft">");
sbdiv.Append("<input class="tbname" type="text" value="XXX表" />");
sbdiv.Append("</span>");
sbdiv.Append("");
sbdiv.Append("");
sbdiv.Append("</div>
");
sbdiv.Append("
<div class="titletr" title="">");
sbdiv.Append("<span class="spleft">");
sbdiv.Append("名:<input class="tbname" type="text" value="tb_Name" />");
sbdiv.Append("</span>");
sbdiv.Append("");
sbdiv.Append("<span class="spright">删除</span>");
sbdiv.Append("</div>
");
sbdiv.Append("

"); sbdiv.Append(""); sbdiv.Append(""); sbdiv.Append("");
sbdiv.Append("");
sbdiv.Append("类型");
sbdiv.Append("");
sbdiv.Append("");
sbdiv.Append("默认值");
sbdiv.Append("");
sbdiv.Append("");
sbdiv.Append(" 备注");
sbdiv.Append("");
sbdiv.Append("
");
sbdiv.Append("");
sbdiv.Append("");
sbdiv.Append("
"); sbdiv.Append("");
sbdiv.Append("");
sbdiv.Append("
<div class="sortdiv">
<div class="sortdiv">");
sbdiv.Append("<span class="selspan"><span class="selspan">");
sbdiv.Append("
<select class="selsort">");</select>
<select class="selsort">sbdiv.Append("</select>
<select class="selsort"><option value="int">int</option></select></span></span>");
sbdiv.Append("

</div>
</div>
varchar(50)
<div class="sortdiv">
<div class="sortdiv">

");
sbdiv.Append("

</div>
</div>
nvarchar(200)
<div class="sortdiv">
<div class="sortdiv">

");
sbdiv.Append("

</div>
</div>
datetime
<div class="sortdiv">
<div class="sortdiv">

");
sbdiv.Append("

</div>
</div>
float
<div class="sortdiv">
<div class="sortdiv">

");
sbdiv.Append("

</div>
</div>
text
<div class="sortdiv">
<div class="sortdiv">

");
sbdiv.Append("

</div>
</div>
ntext
<div class="sortdiv">

");
sbdiv.Append("

");
sbdiv.Append("

");
sbdiv.Append("<input class="sortinput" type="text" value="int" />");
sbdiv.Append("</div>
");
sbdiv.Append("

");
sbdiv.Append("");
sbdiv.Append("<input class="txtdef" type="text" />");
sbdiv.Append("");
sbdiv.Append("");
sbdiv.Append("<input type="text" />");
sbdiv.Append("");
sbdiv.Append("
");
sbdiv.Append(" +");
sbdiv.Append("");
sbdiv.Append("");
sbdiv.Append("");
sbdiv.Append("
<table class="tablelist">
<tbody>
<tr>
<td>");
sbdiv.Append("字段名");
sbdiv.Append("</td>
</tr>
<tr>
<td>");
sbdiv.Append("<input type="text" />");
sbdiv.Append("</td>
</tr>
</tbody>
</table>
");
ViewBag.div = sbdiv.ToString();
ViewBag.AllData = "";
ViewBag.baseId = "";
ViewBag.baseName = "basename";
ViewBag.baseDes = "备注说明";

return View();

}
//响应下载脚本请求,返回txt文件
public void DataText()
{
if (Request["param"] == null)
return;
string t = Request["param"];
if (t == "")
return;
string tbName = "数据库-" + System.DateTime.Now.ToString("yyMMdd-HHmmss")+".sql";
Response.AppendHeader("Content-Disposition", "attachment;filename=" + tbName);
Response.ContentEncoding = System.Text.Encoding.Default;
Response.ContentType = "text/plain";
Response.Write(t);
}

没时间分段说了, 有源码和在线暂时测试地址:http://websql.puworld.com

 

 

源码:http://files.cnblogs.com/mochen/WebSql.zip

[转载]手写代码生成器 - 汉城节度使 - 博客园

mikel阅读(979)

[转载]手写代码生成器 – 汉城节度使 – 博客园.

初来咋到,小试了手写代码生成器,望大牛们指点,下面是成型效果图

需求:

1、采用ORM(对象映射关系)模式;

2、主要解决提供现有表结构,生成Model、DAL层;

不多说了,下面进入正题

/// <summary>
        /// 读取文件
        /// </summary>
        /// <param name="fileName">文件名</param>
        /// <returns>返回文件信息</returns>
        private static string GetConfigFilePath(string fileName)
        {
            string currenctDir = AppDomain.CurrentDomain.BaseDirectory;//存放路径
            string configFile = System.IO.Path.Combine(currenctDir, fileName);
            return configFile;
        }

读取文件

窗体加载时,读取默认路径连接数据库文件信息

void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            string strcon = File.ReadAllText(GetConfigFilePath("connstr.txt"), Encoding.Default);//字符串以默认标准格式读取
            string[] str = strcon.Split(';');
            for (int i = 0; i < str.Count(); i++)
            {
                switch (i)
                {
                    case 0:
                        txtDataSource.Text = str[i].Split('=')[1];//数据库地址
                        break;
                    case 1:
                        txtDatabase.Text = str[i].Split('=')[1];//数据库名称
                        break;
                    case 2:
                        txtUserid.Text = str[i].Split('=')[1];//用户
                        break;
                    case 3:
                        txtPassword.Password = str[i].Split('=')[1];//密码
                        break;
                    default:
                        break;
                }
            }
        }

窗体加载

connstr.txt 文件信息

data source=10.10.198.111;database=systemconfig;user id=sa;password=sa

private void btnConnect_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                //查询所有表名称
                DataTable table = ExcuteDataTable(@"SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES 
                        WHERE TABLE_TYPE = 'BASE TABLE'");
                tables = new List<string>();
                for (int i = 0; i < table.Rows.Count; i++)
                {
                    DataRow row = table.Rows[i];
                    tables.Add(row["TABLE_NAME"].ToString());
                }
                if (tables != null && tables.Count > 0)
                {
                    cmbTables.ItemsSource = TablesDesc(tables);
                    cmbTables.IsEnabled = true;
                    btnGenerateCode.IsEnabled = true;
                    cmbTables.SelectedIndex = 0;
                }
            }
            catch (Exception exc)
            {
                MessageBox.Show("连接失败" + exc.Message);
                return;
            }
            string configFile = GetConfigFilePath("connstr.txt");
            File.WriteAllText(configFile, strconn);//创建一个新的文件,如果存在则覆盖
        }

连接数据库

private void btnGenerateCode_Click(object sender, RoutedEventArgs e)
        {
            string tableName = cmbTables.SelectedItem.ToString();
            if (ckbTable.IsChecked == true)//判断是否指定表生成
            {
                tableName = txtTableName.Text;
                if (string.IsNullOrEmpty(tableName))
                {
                    MessageBox.Show("请输入表名!");
                    return;
                }
                else
                {
                    if (!tables.Contains(tableName))
                    {
                        MessageBox.Show("您输入表名的不存在!");
                        return;
                    }
                }
            }
            CreatModelCode(tableName);//生成Model文件
            CreatDALCode(tableName);//生成DAL文件
        }

生成代码

private void CreatModelCode(string tablename)
        {
            //根据表列名创建Model层属性
            DataTable table = ExcuteDataTable("select top 0 * from " + tablename);
            string Retable = tablename;
            if (tablename.Contains("_"))
            {
                Retable = tablename.Split('_')[1];
            }
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("using System;");
            sb.AppendLine("using System.Collections.Generic;");
            sb.AppendLine("using System.Text;");

            sb.Append("public class ").AppendLine(Retable + "Dto").AppendLine("{");
            foreach (DataColumn item in table.Columns)
            {
                sb.Append("public ").Append(RemoveSystem(GetDataType(item))).
                    Append(" ").Append(item.ColumnName).AppendLine(" {get;set;}");
            }
            sb.Append("}");
            txtModelCode.Text = sb.ToString();
            string configFile = GetConfigFilePath(Retable + "Dto.cs");//创建ModelDto文件
            File.WriteAllText(configFile, sb.ToString());
        }

创建Mode层代码

private DataTable ExcuteDataTable(string sql)
        {
            strconn = @"data source=" + txtDataSource.Text + ";database=" + txtDatabase.Text + ";user id="
               + txtUserid.Text + ";password=" + txtPassword.Password;
            using (SqlConnection cnn = new SqlConnection(strconn))//连接数据库
            {
                cnn.Open();
                using (SqlCommand cmd = cnn.CreateCommand())
                {
                    cmd.CommandText = sql;//执行sql
                    DataSet dataset = new DataSet();
                    SqlDataAdapter dapter = new SqlDataAdapter(cmd);
                    dapter.FillSchema(dataset, SchemaType.Source);
                    dapter.Fill(dataset);//将dataset添加到SqlDataAdapter容器中
                    return dataset.Tables[0];
                }
            }
        }

查询表信息

/// <summary>
        /// 判断表中列是否为空处理,范围属性类型
        /// </summary>
        /// <param name="column"></param>
        /// <returns></returns>
        private static string GetDataType(DataColumn column)
        {
            if (column.AllowDBNull && column.DataType.IsValueType)
            {
                return column.DataType + "?";//表字段为空,类属性中添加?
            }
            else
            {
                return column.DataType.ToString();
            }
        }

根据表中各列字段类型返回model属性类型

/// <summary>
        /// 剔除列类型中包含system.字符串
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        private static string RemoveSystem(string str)
        {
            if (str.Contains("System."))
            {
                return str.Replace("System.", "");
            }
            else
            {
                return str;
            }
        }

剔除列类型中包含system.字符串

private void CreatDALCode(string tablename)
        {
            DataTable table = ExcuteDataTable("select top 0 * from " + tablename);
            string Retable = GetTableName(tablename);
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("using System;");
            sb.AppendLine("using System.Collections.Generic;");
            sb.AppendLine("using System.Data;");
            sb.AppendLine("using System.Data.SqlClient;");

            sb.Append("public class ").AppendLine(Retable + "DAL").AppendLine("{");

            //ToModel
            sb.Append("private static ").Append(Retable + "Dto ").AppendLine("ToModel(DataRow row)").AppendLine("{");
            sb.Append(Retable + "Dto " + "dto").Append("=new ").AppendLine(Retable + "Dto();");
            foreach (DataColumn column in table.Columns)
            {
                sb.Append("dto.").Append(column.ColumnName).Append("=(")
                  .Append(RemoveSystem(GetDataType(column))).Append(")SqlHelper.FromDbValue(row[\"")
                 .Append(column.ColumnName).AppendLine("\"]);");
            }
            sb.AppendLine("return dto;");
            sb.AppendLine("}");

            //查询所有
            sb.Append("public static List<").AppendLine(Retable + "Dto> ListALL()").AppendLine("{");
            sb.Append("List<").AppendLine(Retable + "Dto>  lst=new List<" + Retable + "Dto>();");
            sb.Append("DataTable table = SqlHelper.ExecuteDataTable(\"select * from ").AppendLine(tablename + "\");");
            sb.AppendLine("for (int i = 0; i < table.Rows.Count; i++)");
            sb.AppendLine("{");
            sb.AppendLine(Retable + "Dto dto = ToModel(table.Rows[i]);");
            sb.AppendLine("lst.Add(dto);");
            sb.AppendLine("}");
            sb.AppendLine("return lst;");
            sb.AppendLine("}");

            //根据ID获取数据
            sb.Append("public static ").Append(Retable + "Dto ").AppendLine("GetById(string id)");
            sb.AppendLine("{");
            sb.AppendLine("DataTable table = SqlHelper.ExecuteDataTable(\"select * from " + tablename + " where id=@id\", new SqlParameter(\"@id\",id));");
            sb.AppendLine(Retable + "Dto dto=ToModel(table.Rows[0]);");
            sb.AppendLine("return dto;");
            sb.AppendLine("}");

            //删除
            sb.AppendLine("public static void Delete(string id)");
            sb.AppendLine("{");
            sb.AppendLine("SqlHelper.ExecuteNonQuery(\"delete from " + tablename + " where id=@id\", new SqlParameter(\"@id\",id));");
            sb.AppendLine("}");

            //停用(软删除)
            sb.AppendLine("public static void UnUser(string id)");
            sb.AppendLine("{");
            sb.AppendLine("SqlHelper.ExecuteNonQuery(\"update " + tablename + "set status=1 where id=@id\",new SqlParameter(\"@id\",id));");
            sb.AppendLine("}");

            //启用
            sb.AppendLine("public static void User(string id)");
            sb.AppendLine("{");
            sb.AppendLine("SqlHelper.ExecuteNonQuery(\"update " + tablename + "set status=0 where id=@id\",new SqlParameter(\"@id\",id));");
            sb.AppendLine("}");

            //新增
            sb.AppendLine("public static void Insert(" + Retable + "Dto dto)");
            sb.AppendLine("{");
            sb.AppendLine("SqlHelper.ExecuteNonQuery(\"insert into " + tablename + "(" + GetCoulmns(tablename) + ") values (" + GetValues(tablename) + ")\"," + GetSqlParameter(tablename, true) + ");");
            sb.AppendLine("}");

            //编辑
            sb.AppendLine("public static void Update(" + Retable + "Dto dto)");
            sb.AppendLine("{");
            sb.AppendLine("SqlHelper.ExecuteNonQuery(\"update " + tablename + "set" + GetUpdateValues(tablename) + " where id=@id\"," + GetSqlParameter(tablename, false) + ");");
            sb.AppendLine("}");
            sb.AppendLine("}");
            txtDALCode.Text = sb.ToString();
            string configFile = GetConfigFilePath(Retable + "DAL.cs");
            File.WriteAllText(configFile, sb.ToString());
        }

生成数据处理层代码

private string GetUpdateValues(string tablename)
        {
            DataTable table = ExcuteDataTable("select top 0 * from " + tablename);
            string Retable = GetTableName(tablename);
            List<string> strs = new List<string>();
            for (int i = 0; i < table.Columns.Count; i++)
            {
                if (table.Columns[i].ColumnName.ToLower() != "id" && table.Columns[i].ColumnName.ToLower() != "status")//id、status字段不做更新
                {
                    strs.Add(table.Columns[i].ColumnName + "=@" + table.Columns[i].ColumnName);
                }
            }
            return string.Join(",", strs);
        }

编辑操作,更新列值

private string GetCoulmns(string tablename)
        {
            DataTable table = ExcuteDataTable("select top 0 * from " + tablename);
            string Retable = GetTableName(tablename);
            List<string> strs = new List<string>();
            for (int i = 0; i < table.Columns.Count; i++)
            {
                strs.Add(table.Columns[i].ColumnName);
            }
            return string.Join(",", strs);
        }

获取表中所以列拼接

private string GetValues(string tablename)
        {
            DataTable table = ExcuteDataTable("select top 0 * from " + tablename);
            string Retable = GetTableName(tablename);
            List<string> strs = new List<string>();
            for (int i = 0; i < table.Columns.Count; i++)
            {
                if (table.Columns[i].ColumnName == "id")
                {
                    strs.Add("newid()");
                }
                else if (table.Columns[i].ColumnName.ToLower() == "status")
                {
                    strs.Add("0");
                }
                else
                {
                    strs.Add("@" + table.Columns[i].ColumnName);
                }
            }
            return string.Join(",", strs);
        }

获取更新或插入的列值
private string GetSqlParameter(string tablename, bool isInsert)
        {
            DataTable table = ExcuteDataTable("select top 0 * from " + tablename);
            string Retable = GetTableName(tablename);
            List<string> strs = new List<string>();
            for (int i = 0; i < table.Columns.Count; i++)
            {
                if (isInsert)
                {
                    if (table.Columns[i].ColumnName.ToLower() != "id" && table.Columns[i].ColumnName.ToLower() != "status")
                    {
                        strs.Add("new SqlParameter(\"@" + table.Columns[i].ColumnName + "\", dto." + table.Columns[i].ColumnName + ")");
                    }
                }
                else
                {
                    if (table.Columns[i].ColumnName.ToLower() != "status")
                    {
                        strs.Add("new SqlParameter(\"@" + table.Columns[i].ColumnName + "\", dto." + table.Columns[i].ColumnName + ")");
                    }
                }
            }
            return string.Join(",", strs);
        }

获取SqlParameter参数集

其他忽略部分,作为优化用

/// <summary>
        /// 表名排序
        /// </summary>
        /// <param name="s"></param>
        private List<string> TablesDesc(List<string> lsttable)
        {
            var query = from s in lsttable orderby s ascending select s;
            List<string> tables = new List<string>();
            foreach (string item in query)
            {
                tables.Add(item);
            }
            return tables;
        }

        /// <summary>
        /// 获取表名后缀
        /// </summary>
        /// <param name="tableName"></param>
        /// <returns></returns>
        private string GetTableName(string tableName)
        {
            if (!tableName.Contains("_"))
            {
                return tableName;
            }
            return tableName.Split('_')[1];
        }