[转载]用Visual Studio实践敏捷测试(三)上

mikel阅读(1184)

[转载]用Visual Studio实践敏捷测试(三)上 – 服务世界 开发未来 – 博客园.

上一篇中, 我们讨论了敏捷开发流程中的一些由手动执行的测试任务。手动测试是需要人工完成的测试,被广泛应用于各类产品的各种测试任务中,而与之相对应还有自动化测 试,即通过程序自动运行完成测试任务。自动化测试能帮助开发团队节省测试运行的人工、提高开发效率。接下来在本篇中,我想和大家讨论一下敏捷开发中手动测 试和自动化测试之间的关系以及如何实现和利用自动化测试。

手动测试的特点

由于手动测试依赖于人工操作,很自然的存在着不确定性,每一次的操作都可能或多或少有一些不同。这种不确定性既是手动测试的优点,也为其带来了一定的局限 性。从优点的角度来说,手动测试更加灵活多变,可能在不经意间就采用了一种全新的操作序列,或者将产品带入一个意料之外的状态。这有助于深入测试产品功能 的细节,覆盖那些常规思路无法到达的用户场景。而从另一个角度来看,手动测试的不确定性也使得其测试覆盖的内容无法预料,在需要保障测试的覆盖面时,就显 得有些不足了。此外,手动测试也不适应于大规模、大数据量、长时间或者是多平台的测试,人工完成这样大的工作量是困难且没有必要的。

手动测试的这些特点就导致其适用于那些允许灵活甚至是要求灵活的小规模的测试任务,比如伙伴测试就是个很好的例子——测试内容不那么死板,期望测试执行人有一些创造性。

自动化测试

自动化测试充分利用了计算机“任劳任怨”执行人类给它的指示的特点,用程序实现需要的测试,此后该测试就可以被多次自动执行。在测试会被大量运行的前提 下,这不仅能节省重复人工劳动,更重要的是提供了一种迅速而准确的保证测试覆盖的方法。特别是在敏捷开发紧张的节奏下,自动化测试的这些特质能起到十分显 著的加快开发进程的作用。

那么什么样的测试需要自动化呢?

最显然的是诸如压力测试等需要大量操作的测试场景。比如我们常常要求产品能承受连续8个小时以上的各种操作,这样大强度的压力测试并不适合人由工完成,显然自动化测试能更好的完成该项任务。

接下来是那些核心的功能和主要的用户场景,这些是产品最需要保护的部分,所以我们希望能在代码改变的情况下及时地运行相关的测试,以保证它们不被破 坏。在敏捷开发高速运转、频繁签入的状态下,自动化这些测试以便能将其在封闭签入(Gated check-in)等测试运行中迅速自动执行、及时发现问题,能在很大程度上保证产品开发流程的顺利进行。同时,将这些重要的测试自动化也能避免手动测试 的“偷工减料”问题,计算机总是会忠实的执行每一个测试步骤的。

此外还有一些从其本身特性而言不适合手动执行的特殊类型的测试,比如模糊测试(Fuzzing Test)要求大量生成随机数据一一对产品试验,也是自动化测试能大展身手的地方。

自动化测试的度

在这里我想提醒大家的是测试的自动化的程度并不是越高越好。在我的开发团队中,我们曾经将测试计划中高达95%以上的测试都自动化。这样的带来的显著优势 是自动的测试运行提供了很好的覆盖率,从而保证了各项功能都能保持正确工作。但是,也带来了一些问题:首先,编写自动化测试程序需要大量的时间,很多时候 让程序自动操作执行产品功能特别是产品UI是件很困难的事情,需要大量的时间和精力来实现测试程序本身。其次,当产品功能变化时,修改测试还要花费额外的 成本,不像手动测试那样随时都可以更改。另外,自动化测试还有一个容易被忽视却十分重要的问题——远离了用户体验。当测试被“傻瓜式”自动执行时,我们根 本无法得知其相对应的用户场景的使用感。举个简单的例子,再不方便使用的功能如果只写一次程序我们多半是可以忍受的,但是如果要手动执行,恐怕三五次就会 让人受不了了。所以把握手动测试和自动化测试的比例是颇值得研究的一个话题。

在意识到过量的自动化测试会使其从辅助开发的工具变为团队的负担和障碍之后,我们调整了自动化测试的策略,采取了一种渐进式添加自动化测试的方案。在产品 生命周期的前期,功能快速的被添加且行为随时可能改变,此时对测试的需求偏向于尽可能迅速完成对新功能的测试,自动化测试所需的建立测试框架以及在功能改 变时修改代码的代价在此时显得过于昂贵了,所以我们更倾向于做较多手动测试,只实现少量简单而核心的自动化测试。而随着产品功能的不断添加和趋于稳定,不 再有大量的新功能需要测试,而更多的需要用于保护已有功能不被破坏的自动化测试,此时可以陆续添加测试计划中那些之前没有时间自动化的测试用例。在产品生 命周期的末期,特别是产品将有多个版本时,准备全面的自动化测试覆盖以保护产品功能不被破坏则成为主要的测试任务之一。

林俊彦

软件测试开发工程师

[转载]使用 IconHandler 2.0 获取文件图标

mikel阅读(1003)

[转载]使用 IconHandler 2.0 获取文件图标 – 鹤冲天 – 博客园.

在 Windows 资源管理器中,我们可以使用图标方式来查看文件,图标可以让我们快速判断文件的类型,如下图中所选中的文件,相信大家一眼就能看出是一个 Word 文档。

icon1

设计美观的图标让我们赏心悦目,如果可以把这些小图标直接用在我们写的程序中,一定会增色不少。

但 .Net 并没有直接提供给我们一个可以直接获取文件图标的办法,如果使用搜索引擎查找解决办法的话,你会找到很多解决办法,但大多是以零散的代码提供的,质量和来源都没有保证。

我找到一位国外大牛写的 IconHandler,分享给大家!

IconHandler

IconHandler 是由 MVolo 分享的一个类库,目前为 2.0 版本,网址为:

http://mvolo.com/blogs/serverside/archive/2008/04/27/IconHandler-2.0-File-icons-in-ASP.NET-applications.aspx

下载后打开压缩包里面如下包含两个 dll 文件:

IconHandler2

第一个文件 ShellIcons.dll 用于从系统中获取图标。第二个文件 ShellIconHandler.dll 用于在 ASP.NET 显示图标。先看第一个文件。

ShellIcons.dll

获取图标我们只需要调用 ShellIcons.GetIconForFile 静态方法,这个方法有三个参数:

public class ShellIcons
{
    public static IconContainer GetIconForFile(string path, bool useExtension, bool largeIcon);
}

先来看两段代码,这三个参数的含义就自然明白了:

//WinForm:获取 rar 文件的大图标并显示在 pictureBox1 上
using (var container = ShellIcons.GetIconForFile(".rar", true, true))
{
    Icon rarFileLargeIcon = container.Icon;
    Bitmap rarFileLargeBmp = rarFileLargeIcon.ToBitmap();
    pictureBox1.Image = rarFileLargeBmp;
}
//WinForm:WinForm:获取 Fiddler.exe 的小图标并显示在 pictureBox2 上
using (var container = ShellIcons.GetIconForFile(@"C:\Program Files\Fiddler2\Fiddler.exe", false, false))
{
    pictureBox2.Image = container.Icon.ToBitmap();
}

显示如下:

ShellIcons

ShellIconHandler.dll

这个 dll 用在 ASP.NET 中显示图片,我们先看作者文件中的一张图片:

IconHandler-PPTPreview

这个调用酷吧,使用也比较简单,首先引用上面这两个 dll 文件,然后修改 web.config 文件(加入带下划线的部分):

<configuration>
    <!-- ShellIconHandler configuration section declaration -->
     <configSections>
        <section name="iconHandler" type="Mvolo.ShellIcons.Web.ShellIconHandlerConfigurationSection" />
    </configSections>
    <system.webServer>
        <!-- Add IconHandler for IIS 7.0 Integrated mode -->
        <handlers>
            <add name="iconhandler" path="geticon.axd" verb="GET" type="Mvolo.ShellIcons.Web.ShellIconHandler" />
        </handlers>
        <validation validateIntegratedModeConfiguration="false" />
    </system.webServer>
    <system.web>
        <!-- Add IconHandler for IIS 6.0 / IIS 7.0 Classic mode -->
        <httpHandlers>
            <add path="geticon.axd" verb="GET" type="Mvolo.ShellIcons.Web.ShellIconHandler" />
        </httpHandlers>
    </system.web>
    <!--
    Icon Handler by Mike Volodarsky
    Retrieves the shell icon for the specified file name.
    -->
    <iconHandler enabled="true"
            alwaysUseExtension="true"
            enableClientCaching="true"
            enableServerCaching="true" />
</configuration>

OK 了,我们在首页显示几个图标试试:

<img src="geticon.axd?file=.docx&size=small" alt=".docx" />
<img src="geticon.axd?file=.xlsx&size=small" alt=".xlsx" />
<img src="geticon.axd?file=.pptx&size=small" alt=".pptx" />
<br />
<img src="geticon.axd?file=.docx" alt=".docx" />
<img src="geticon.axd?file=.xlsx" alt=".xlsx" />
<img src="geticon.axd?file=.pptx" alt=".pptx" />

显示如下:

IconHandler-view

效果不错吧! (还可以在配置文件中,通过 iconHandler 节的选项来指定是否进行缓存。)

写到这里,也许有的朋友会问,网站通常部署在服务器上,如果服务器上没有安装 Office 这些图标能正确显示吗?很不幸,不能显示,IconHandler 通过 shell API SHGetFileInfo 来获取图标,如果文件类型没有在系统中注册的话,是获取不到的。但庆幸的是 MVolo 考虑到了这个问题,并给我们供了一个解决办法:

在 Web 项目中新建立一个名为 /App_Resources/Icons 的文件夹中,将图片以下面的方式放入:

app_resources_icons

还要再修改下 iconHandler 配置节(加入带下划线的部分):

<configuration>
    <iconHandler enabled="true"
                alwaysUseExtension="true"
                enableClientCaching="true"
                enableServerCaching="true"
                useSavedIcons="true" />
</configuration>

我们再测试一下,在首页中加入以下代码:

<img src="geticon.axd?file=.hctx&size=small" alt=".hctx" />
<img src="geticon.axd?file=.hctx" alt=".hctx" />
<br />

预览如下:

IconHandler-view2

解决了,但实际文件类型可能有好几百甚至上千种,一个个来太麻烦,幸好 MVolo 也为我们提供了另外一个工具。

IconGen.exe

使用这个工具我们可以批量生成文件对应的大小图标,这是一个控件台应用程序,只需要执行以下两个命令:

> IconGen.exe c:\Icons large
> IconGen.exe c:\Icons small

分别生成大小图标,如下:

IconGen

大大小小一共生成了 2038 个图标!

相关下载: IconHandler 2.0 IconGen.exe

[转载]ASP.NET MVC中对Model进行多步验证的解决方法

mikel阅读(925)

[转载]ASP.NET MVC中对Model进行多步验证的解决方法 – 海纳百川 – 博客园.

在我之前的博文:ASP.NET MVC2.0结合WF4.0实现用户多步注册流程中 将一个用户的注册分成了四步,而这四个步骤都是对一个Model操作的。当时我加上ModelState.IsValid这句验证代码的时候,根本没法通 过验证,因为在注册的前面三步,注册用户的Model信息都没填写完整。当时很纠结,因为刚接触ASP.NET MVC,故没有找到解决方案。我想现在给出解决的办法也为时不晚。看下面需要验证的Model的代码如下:

代码

public class UserViewModel { [DisplayName("step")] [Required(ErrorMessage = "You must select a step .")] public int Step { get; set; } //个人信息 [Required(ErrorMessage = "姓名不能为空")] [StringLength(20, ErrorMessage = "姓名长度不能超过20个字符")] public string Name { get; set; } [RegularExpression(@"120|((1[0-1]|\d)?\d)", ErrorMessage = "年龄格式不对")] public int? Age { get; set; } //职位信息 [Required(ErrorMessage = "职位不能为空")] public string Post { get; set; } public int? Salary { get; set; } //学历信息 [Required(ErrorMessage = "毕业院校不能为空")] public string University { get; set; } public int? GraduationYear { get; set; } //联系信息 [Required(ErrorMessage = "邮件不能为空")] [RegularExpression(@"^[a-z][a-z|0-9|]*([_][a-z|0-9]+)*([.][a-z|" + @"0-9]+([_][a-z|0-9]+)*)?@[a-z][a-z|0-9|]*\.([a-z]" + @"[a-z|0-9]*(\.[a-z][a-z|0-9]*)?)$", ErrorMessage= "邮件格式不正确")] public string Email { get; set; } public int? Mobile { get; set; } public IEnumerable<SelectListItem> StepList { get; set; } public UserViewModel() { var list = new List<SelectListItem>() { new SelectListItem { Text = "(Select)" }, new SelectListItem { Value = "1", Text = "Step1" }, new SelectListItem { Value = "2", Text = "Step2" }, new SelectListItem { Value = "3", Text = "Step3" }, new SelectListItem { Value = "4", Text = "Step4" } }; this.StepList = new SelectList(list, "Value", "Text"); } }

实现:

这篇文章这种情况服务端和客户端的验证都会讲到。为了简化起见,这里我除去的WF的流程功能,直接用下拉框表示,当下拉框选择step1表示填写第一步注 册的信息,当下拉框选择step2表示填写第二步注册的信息,当下拉框选择step3表示填写第三步注册的信息,当下拉框选择step4表示填写第四步注 册的信息。这个很容易实现,我使用JQuery来显示和隐藏下拉框对应的step,JQuery代码如下:

代码

<script type="text/javascript"> $(function () { $.fn.enable = function () { return this.show().removeAttr("disabled"); } $.fn.disable = function () { return this.hide().attr("disabled", "disabled"); } var dllStep = $("#Step"); var step1 = $("#Step1,#Step1 input"); var step2 = $("#Step2,#Step2 input"); var step3 = $("#Step3,#Step3 input"); var step4 = $("#Step4,#Step4 input"); setControls(); dllStep.change(function () { setControls(); }); function setControls() { switch (dllStep.val()) { case "1": step1.enable(); step2.disable(); step3.disable(); step4.disable(); break; case "2": step1.disable(); step2.enable(); step3.disable(); step4.disable(); break; case "3": step1.disable(); step2.disable(); step3.enable(); step4.disable(); break; case "4": step1.disable(); step2.disable(); step3.disable(); step4.enable(); break; case "": step1.disable(); step2.disable(); step3.disable(); step4.disable(); break; } } }); </script>

如下图:

第一步:填写姓名和年龄。

第二步:填写职位和薪水

第三步填写:毕业院校和毕业时间

第四步填写:邮箱和电话

为了实现这样的验证,我们可以将验证的错误信息中移除不在当前步骤填写的字段的错误信息,写一个类InputValidationModelBinder 继承DefaultModelBinder并重载OnModelUpdated方法,将不必要的错误信息清除,代码如下:

public class InputValidationModelBinder : DefaultModelBinder { protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext) { var modelState = controllerContext.Controller.ViewData.ModelState; var valueProvider = controllerContext.Controller.ValueProvider; var keysWithNoIncomingValue = modelState.Keys.Where(x => !valueProvider.ContainsPrefix(x)); foreach (var key in keysWithNoIncomingValue) modelState[key].Errors.Clear(); } }

上面是服务端的代码,对于客户端,我们都知道ASP.NET MVC客户端验证时通过MicrosoftMvcValidation.js去实现的。看下面代码。

1 validate: function Sys_Mvc_FormContext$validate(eventName) { 2 var fields = this.fields; 3 var errors = []; 4 for (var i = 0; i < fields.length; i++) { 5 var field = fields[i]; 6 if (!field.elements[0].disabled) { 7 var thisErrors = field.validate(eventName); 8 if (thisErrors) { 9 Array.addRange(errors, thisErrors); 10 } 11 } 12 } 13 if (this.replaceValidationSummary) { 14 this.clearErrors(); 15 this.addErrors(errors); 16 } 17 return errors; 18 } 19 }

在第6行代码加入了一句判断:当页面的元素没有被disabled的时候才验证。

好了这样就实现了一次只对Model中的几个属性字段进行验证。

运行:

ASP.NET mvc的验证机制只对model中当前页面的属性进行验证:

填写正确通过验证:

总结:

[转载]在ActionScript 3中动态加载类

mikel阅读(1034)

[转载]在ActionScript 3中动态加载类 — Windows Live.

在ActionScript 3中动态加载类

Loading classes dynamically in ActionScript 3 is easy, but there are a couple of tricks to keep in mind.

ActionScript 3动态加载类很容易。有一些窍门便于使用。

First of all, to load a class dynamically, use the flash.utils.getDefinitionByName function. Be sure to pass in the fully qualified class name, like this:

首先,加载一个动态类可以使用flash.utils.getDefinitionByName 方法。确保使用的是完整的类名,例如:

var MyClass:Class = getDefinitionByName(“com.mydomain.package.MyClass”) as Class;

var myInstance:MyClass = new MyClass();

Easy enough. But there’s something to watch out for. Class definitions only get compiled into your SWF if they’re declared somewhere in your code. In other words, if MyClass was never declared, the class definition wouldn’t be included in your SWF, and you would get a runtime error when trying to load it dynamically. (Note that this is not the case for classes native to the runtime — only your own custom classes.) Even importing the class isn’t enough to get it included — it must actually be declared.

很容易做到。但是有一些需要注意。如果他们使用在代码的某个地方,类的声明只编译到您的SWF中。换句话说,当它试图动态加载时,如果MyClass从未引用,类定义将不包括在您的SWF,你会得到一个运行时错误。 (请注意,这不是对原生类(即FLASH PLAYER自带的)情况只有你自己的自定义类。)即使引入类是不够的,得将它包含进去它实际上必须调用。

There are two ways to work around this issue:

有两种方法解决这个问题:

1. Declare the class somewhere in the code.在代码中的某个位置声明这个类

2. Use a command line argument to force the class definition to be included.Below is an example of declaring the class before loading it dynamically:使用命令行参数来强制类定义应包括在内。

下面是一个动态加载它之前宣布的类的例子:

MyClass;

// or

var mc:MyClass;

var MyClass:Class = getDefinitionByName(“com.mydomain.package.MyClass”) as Class;

var myInstance:MyClass = new MyClass();

This works fine, but in my opinion, it may defeat the purpose of loading a class dynamically. Usually you want to load classes dynamically because you’re not sure until runtime which class you’re going to want. If you knew which classes you wanted before you loaded them dynamically, you could just instantiate them directly.

The other option is to use the mxmlc -includes compiler argument like this:

这个工作得很好,但在我看来,它可以达到动态加载一个类的目的。通常你要动态加载的类,因为不是在你当前的代码中,直到运行时你会想知道。如果你想知道哪些类,然后再加载它们的动态,你可以只直接实例。
另一种选择是使用mxmlc –includes 编译器这样:

-includes=com.mydomain.package.MyClass

Now, the class definition will be compiled into your SWF, and you can load it dynamically anytime you want without ever having to declare it.

现在定义的类被编译到你的SWF中,你可以在任何没有声明他的情况下,随时动态加载它。

To specify compiler arguments in Flash Builder, follow these steps:

具体在Flash Builder中的编译参数,遵循下列步骤:

1. Right-click on your project. 右键你的项目

2. Choose “Properties”.选择“属性”

3. Select “Flex Compiler”.选择“Flex 编译”

4. Enter your arguments in the “Additional compiler arguments” section.输入你的参数在“Additional compiler arguments”选项中

As an aside, you can use the flash.utils.getQualifiedClassName andflash.utils.getQualifiedSuperclassName functions to get the fully qualified class name of an object instance.

同时你可以使用

flash.utils.getQualifiedClassName flash.utils.getQualifiedSuperclassName 方法获得完整的类名和对象实例。

[转载]七大.NET著名开源项目

mikel阅读(1065)

[转载]百万开发者拥戴!七大.NET著名开源项目 – CSDN.NET – CSDN软件研发频道.

尽管过了相当长的时间,花费了不少资源,微软和.NET社区还是在最近几年加入到了开源运动的阵营中来了,这令人相当惊讶,因为两大阵营一直都是经 常对立的。然而,事实是依靠开源,微软社区中的开源开发工具日益发展壮大。本文将深入了解.NET领域的开源项目,介绍七个在全球受到数百万.NET开发 者拥戴和欢迎的开源项目。

AJAX 控件工具包

仅仅五年时间,自从AJAX的概念提出来以后,使用JavaScript,XML和异步通信去提升用户的在线体验已经从新颖的想法变为实在的需要。 因为AJAX驱动的要求已经变得很普通,不久开发者们都投入到开发AJAX框架中去,这使得像日历和客户端表单验证这样的功能变得很容易实现。

.NET开发者在处理AJAX方面有ASP.NET AJAX Control Toolkit工具包这个很好的解决方案。它由微软公司和开发者社区联合开发,提供了多种功能强大的控件,如评分控件,模态弹出窗口,密码强度验证器,可拖拉的面板。

可以查看ASP.NET主页上该项目的主页,上面有控件示例的清单和例子,除此之外,还有大量的导学文章,能指导如何充分利用工具包的很多特性。

ASP.NET MVC

MVC的架构在现在的Web开发中起着重要的作用,象Ruby on Rails,Zend Framwork等框架都是使用MVC框架进行开发的。多亏有了ASP.NET MVC框架,.NET开发者也能够拥有一个功能强大的MVC框架了。ASP.NET MVC框架提供了MVC的良好分层,测试驱动以及“约定优先”的最佳实践,为.NET开发者构建复杂的网页提供了强大的解决方案。因为是基于.NET构建 的,开发者能充分利用ASP.NET的诸如数据缓存和表单验证等功能。你还可以发现不少其他项目也是基于ASP.NET MVC构建,并且提升了其功能,象MvcContrib,它扩展了ASP.NET MVC的功能,还包括象UI helper和模型绑定器等功能。

ASP.NET MVC框架的网站提供给新手一系列学习资源,包括了视频系列导学,它们通过示范搭建应用程序的方法去指导读者学习。

CruiseControl

现在软件的复杂性使得软件开发团队不得不重新考虑软件开发的过程,这要引入既能缩短开发时间又能提高软件质量的方法。这使得出现了大量的软件开发最 佳实践,它们越来越广泛应用在企业中,象测试驱动和持续集成。其中持续集成在整个软件开发过程中,通过有计划和严格地整合工程的组件,并能尽可能减少在这 方面付出的时间和精力。这使得开发者能尽早发现和解决在整合过程中的问题,而不是等问题变得越来越复杂再去解决。

更准确来说,持续集成包含了软件开发过程中的自动化过程,包括定期运行测试,在每次开发者向工程代码库提交代码时,重新编译构建项目。因为这个自动 化的阶段包含了大量的象文件移动等操作,近年来,很多能完成自动持续化集成的解决方案出现了,其中包括开源项目 CruiseControl,CruiseControl提供了自动化构建的工具,可以查看构建的结果,并且生成报告。CruiseControl现在已 变成一个十分流行的持续集成解决方案。在众多的第三方持续集成工具中,可以找到象JCCTray这样的工具,它是一个桌面运行的应用程序,可以直接了解你 的构建服务器的状态,并且BigVisibleCruise这个工具,使用可视化的提示显示构建服务器上所有受管理的工程的状态。

DotNetNuke

随着越来越多的公司正把它们的商务活动迁移到在网上,这需要功能强大的网站内容管理系统,而不仅是简单更新一下网页内容。实际上,现在的网站不仅是 卖商品,同样也管理销售机会和其他合同等,也要维护文档和提供如图片和视频等多媒体服务。最有效去管理这些大量的内容的是采用集中的内容管理系统。其中最 著名的解决方案是DotNetNuke,它是一个功能强大的基于 .NET的内容管理系统,已在全球500强中很多企业和众多开发者中被采用。

DotNetNuke虽然已经有了多个版本,但开源的社区版本提供了数百个特性,如它们能让公司去管理通告,实现论坛交互,而这些都不需要有任何的编程知识。如果公司需要自定义一些新功能的话也能按它们的需要去扩展。

Mono

2002年,.NET 1.0的出现是软件开发领域中的一个分水岭,在众多特性中,它可以让开发者使用任何支持.NET公共运行时的语言去开发应用。当然,微软将WINDOWS 平台作为.NET运行的主要平台,这对象LINUX等其他系统来说是个噩梦。然而因为.NET是基于一系列公开的标准的,一些企业组织的开发者开发了 Mono项目,能让.NET可以运行在非微软平台上。

现在由于Novell公司的大力支持,Mono项目这些年发展得相当迅速,它不但能让.NET跑在非微软的平台上,也支持在LINUX上运行Sliverlight技术(目前代号为Monnlight),甚至开始研究在使用C#语言去编写iPhone上的应用。

可以到Mono的主页上去查看更多的相关信息,除此之外还可以去下载Mono专门为Visual Studio开发的插件工具。

NAnt

无论是JAVA还是.NET的开发者对代码的编译部署过程都是十分熟悉的,这个过程随着工程项目变得越来越复杂,从而也会耗费不少时间。为了应对这 一问题,开发者James Davidson编写了ANT这一自动构建工具,它在JAVA开发者社区阵营中开始应用得越来越普遍,不久,NET阵营中的NANT项目也随之诞生。使用 NANT,你可以建立构建脚本,不论是执行单元测试还是创建文档等其他.NET的构建周期,都可以自动执行这些脚本。

NHibernate

尽管在软件开发中,关系数据库的应用是很普遍了,但开发者和数据库之间还是存在一些壁垒。问题关键在于如何高效地在应用和数据库之间以各自支持的格 式转换数据。开发者尽了很大的努力创造了很多方便转换的工具,其中最著名的是象Hibernate这样的对象关系映射框架(ORM)

.NET开发者在处理ORM方面有NHibernate这样的工具,自从2005年它出现后,广为受到开发者的欢迎。NHibernate能完 成.NET开发者面对的大量枯燥的数据日常操作,不仅在映射应用的模型到数据表上很有作用,而且能根据预先定义好的数据模型去产生表的模型定义。

NHibernate的网站上提供了大量的有用的资源,能帮助你去了解这个强大的ORM解决方案框架。

小结

如果你是.NET开发者而且还没开始进入庞大并且日益发展的开源阵营的话,那么你应该去看看了。花点时间去研究本文提到的几个开源项目,我保证你会 马上看到生产效率的提高的,有可能这还会引领你去向开源社区贡献你自己的开源项目呢,如果你使用了本文没提到的开源项目工具,可以在评论中告诉我。

原文链接:Introducing Seven Popular Open Source Projects for .NET Developers

译文链接:http://tech.it168.com/a2010/0906/1099/000001099775_all.shtml

[转载]以Model为核心的表单设计器的思路

mikel阅读(1048)

[转载]以Model为核心的表单设计器的思路 – 海纳百川 – 博客园.

在之前的文章中介绍过一些表单设计器的思路和想法。在上一篇文章:零代码平台实现中说到我要实现一个功能强大的表单设计工具。经过几天思考,觉得以Model为核心去实现表单设计器思路既简单也很清楚。这篇文章我将谈谈如何以Model为核心去实现表单设计器。

设计思路:

之所以会我想到以Model为核心,主要借鉴ASP.NET MVC框架。ASP.NET MVC中控制器向View提供Model。View将Model渲染成表单。有一点不好的是生成的View中夹杂了HTML和C#代码,没有了 ASP.NET Web Form的用户控件,所以就不能去可视化拖拽了,看下面ASP.NET MVC生成表单的两张图。

简单的List页面:

Detail页面:

从上图中可以看出不存在控件,那就不能可视化的拖拽。

我做这个设计器的目的之一就是解决这个不能拖拽的问题,先对比一下asp.net表单生成机制和我的表单设计器的机制:

从上图可以看出两种机制是差不多的。

mvc中有ModeL和ViewModel两个概念。Model是一般都固定不变的,编译之后就会存在dll,所以通过下面代码使用反射来生成表单。

1 System.Windows.Forms.OpenFileDialog dlg = new System.Windows.Forms.OpenFileDialog(); 2 dlg.AutoUpgradeEnabled = false; 3 if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) 4 { 5 string fileName = dlg.FileName; 6 txtPath.Text = fileName; 7 System.Reflection.Assembly ass; 8 Type[] types; 9 ass = System.Reflection.Assembly.LoadFile(fileName);//要绝对路径 10 //获取所以的实体类 11   types = ass.GetTypes(); 12 foreach(Type type in types) 13 { 14 ListBoxItem li = new ListBoxItem(); 15 li.Content = type.Namespace+"."+type.Name; 16 lbxModel.Items.Add(li); 17 18 } 19 20 21 22 }

对于ViewModel,我计划先使用SQL语句生成ViewModel的结构和代码,然后生表单。

Model方式界面:

ViewModel方式界面:

简单示例:

看个例子,实体类代码如下:

1 public class Person 2 { 3 public string Name 4 { 5 get; 6 set; 7 } 8 9 public int Age 10 { 11 get; 12 set; 13 } 14 15 public string Sex 16 { 17 get; 18 set; 19 } 20 }

在List页面,我生成一个固定的Grid和以下常用的工具栏按钮如:新增、修改、删除…,如下如。

对于修改页面、详情页面等,我生成label+textbox组合方式的表单。

下面我们就可以自由调整界面了:

调整之后的一级界面

调整之后的二级界面

最后通过代码生成机制生成aspx代码。

总结:这个是我思考出的一个表单设计器的解决方案,感觉比较简单而且比较通用,故写这篇文章做为一个分享。考虑不周的地方期待大家多多指正。

[转载]Visual Studio一款免费生成流程图的插件

mikel阅读(2574)

[转载]一款免费生成流程图的插件 – eaglet – 博客园.

最近写文档,要贴流程图,自己画太烦,就到网上去找自动生成流程图的软件,结果知道了这个叫 Microsoft Visual Studio Learning Pack 的插件,用它来生成函数的流程图,感觉很方便。

下载地址

http://www.microsoft.com/downloads/en/details.aspx?displaylang=en&FamilyID=0ce3cbbd-7fc7-410b-8c2c-e18d1c60a6cd

下载后安装,安装完以后打开VS ,在代码中相应函数的函数名处点右键选 Generate Flow Chart 就可以生成这个函数的流程图了,非常方便

image

image

是不是很方便,介绍给大家,对写文档或者学习代码会有些帮助。

[转载]JavaScript 设计模式 :安全沙箱模式

mikel阅读(960)

[转载]JavaScript 设计模式 :安全沙箱模式 – Stand Alone – 博客园.

沙箱模式常见于YUI3 core,它是一种采用同一构造器(Constructor)生成彼此独立且互不干扰(self-contained)的实例对象,而从避免污染全局对象的方法。

命名空间


JavaScript本身中没有提供命名空间机制,所以为了避免不同函数、对象以及变量名对全局空间的污染,通常的做法是为你的应用程序或者库创建一个唯一的全局对象,然后将所有方法与属性添加到这个对象上。

1 /* BEFORE: 5 globals */ 2  // constructors 3  function Parent() {} 4  function Child() {} 5  // a variable 6  var some_var = 1; 7  // some objects 8  var module1 = {}; 9 module1.data = {a: 1, b: 2}; 10  var module2 = {}; 11  /* AFTER: 1 global */ 12  // global object 13  var MYAPP = {}; 14  // constructors 15  MYAPP.Parent = function() {}; 16 MYAPP.Child = function() {}; 17  // a variable 18  MYAPP.some_var = 1; 19  // an object 20  MYAPP.modules = {}; 21  // nested objects 22  MYAPP.modules.module1 = {}; 23 MYAPP.modules.module1.data = {a: 1, b: 2}; 24 MYAPP.modules.module2 = {};

代码清单1 : 传统命名空间模式

在这段代码中,你创建了一个全局对象MYAPP,并将其他所有对象、函数作为属性附加到MYAPP上.

通常这是一种较好的避免命名冲突的方法,它被应用在很多项目中,但这种方法有一些缺点。

1.需要给所有需要添加的函数、变量加上前缀。

2.因为只有一个全局对象,这意味着一部分代码可以肆意地修改全局对象而导致其余代码的被动更新。

全局构造器

你可以用一个全局构造器,而不是一个全局对象,我们给这个构造器起名为Sandbox(),你可以用这个构造器创建对象,你还可以为构造器传递一个回调函数作为参数,这个回调函数就是你存放代码的独立沙箱环境。

1 new Sandbox(function(box){ 2    // your code here... 3  });


代码清单2:沙箱的使用


让我们给沙箱添加点别的特性


1.创建沙箱时可以不使用’new’操作符

2.Sandbox()构造器接受一些额外的配置参数,这些参数定义了生成对象所需模块的名称,我们希望代码更加模块化。


拥有了以上特性后,让我们看看怎样初始化一个对象。


代码清单3显示了你可以在不需要‘new’操作符的情况下,创建一个调用了’ajax’和’event’模块的对象.


1 Sandbox(['ajax', 'event'], function(box){ 2    // console.log(box); 3  });

代码清单3:以数组的形式传递模块名

1 Sandbox('ajax', 'dom', function(box){ 2    // console.log(box); 3  });

代码清单4:以独立的参数形式传递模块名

代码清单5显示了你可以把通配符’*’作为参数传递给构造器,这意味着调用所有可用的模块,为了方便起见,如果没有向构造器传递任何模块名作为参数,构造器会把’*’作为缺省参数传入.

1 Sandbox('*', function(box){ 2    // console.log(box); 3  }); 4 Sandbox(function(box){ 5    // console.log(box); 6  });

代码清单5:调用所用可用模块

代码清单6显示你可以初始化沙箱对象多次,甚至你可以嵌套它们,而不用担心彼此间会产生任何冲突.

1 Sandbox('dom', 'event', function(box){ 2  // work with dom and event 3    Sandbox('ajax', function(box) { 4    // another sandboxed "box" object 5   // this "box" is not the same as 6   // the "box" outside this function 7   //... 8   // done with Ajax 9    }); 10  // no trace of Ajax module here 11  });

代码清单6:嵌套的沙箱实例

从上面这些示例可以看出,使用沙箱模式,通过把所有代码逻辑包裹在一个回调函数中,你根据所需模块的不同生成不同的实例,而这些实例彼此互不干扰独立的工作着,从而保护了全局命名空间。

现在让我们看看怎样实现这个Sandbox()构造器.

添加模块

在实现主构造器之前,让我们看看如何向Sandbox()构造器中添加模块。

因为Sandbox()构造器函数也是对象,所以你可以给它添加一个名为’modules’的属性,这个属性将是一个包含一组键值对的对象,其 中每对键值对中Key是需要注册的模块名,而Value则是该模块的入口函数,当构造器初始化时当前实例会作为第一个参数传递给入口函数,这样入口函数就 能为该实例添加额外的属性与方法。

在代码清单7中,我们添加了’dom’,’event’,’ajax’模块。

1 Sandbox.modules = {}; 2 Sandbox.modules.dom = function(box) { 3   box.getElement = function() {}; 4   box.getStyle = function() {}; 5   box.foo = "bar"; 6 }; 7 Sandbox.modules.event = function(box) { 8  // access to the Sandbox prototype if needed: 9 // box.constructor.prototype.m = "mmm"; 10    box.attachEvent = function(){}; 11   box.dettachEvent = function(){}; 12 }; 13 Sandbox.modules.ajax = function(box) { 14   box.makeRequest = function() {}; 15   box.getResponse = function() {}; 16 };

代码清单7:注册模块

实现构造器

代码清单8描述了实现构造器的方法,其中关键的几个要点:

1.我们检查this是否为Sandbox的实例,若不是,证明Sandbox没有被new操作符调用,我们将以构造器方式重新调用它。

2.你可以在构造器内部为this添加属性,同样你也可以为构造器的原型添加属性。

3.模块名称会以数组、独立参数、通配符‘*’等多种形式传递给构造器。

4.请注意在这个例子中我们不需要从外部文件中加载模块,但在诸如YUI3中,你可以仅仅加载基础模块(通常被称作种子(seed)),而其他的所有模块则会从外部文件中加载。

5.一旦我们知道了所需的模块,并初始化他们,这意味着调用了每个模块的入口函数。

6.回调函数作为参数被最后传入构造器,它将使用最新生成的实例并在最后执行。

1 function Sandbox() { 2    // turning arguments into an array 3    var args = Array.prototype.slice.call(arguments), 4    // the last argument is the callback 5      callback = args.pop(), 6    // modules can be passed as an array or as individual parameters 7      modules = (args[0] && typeof args[0] === "string") ? 8     args : args[0], 9     i; 10      // make sure the function is called 11     // as a constructor 12    if (!(this instanceof Sandbox)) { 13      return new Sandbox(modules, callback); 14   } 15   // add properties to 'this' as needed: 16   this.a = 1; 17   this.b = 2; 18   // now add modules to the core 'this' object 19   // no modules or "*" both mean "use all modules" 20   if (!modules || modules === '*') { 21     modules = []; 22     for (i in Sandbox.modules) { 23       if (Sandbox.modules.hasOwnProperty(i)) { 24         modules.push(i); 25       } 26      } 27    } 28   // init the required modules 29   for (i = 0; i < modules.length; i++) { 30     Sandbox.modules[modules[i]](this); 31   } 32   // call the callback 33   callback(this); 34 } 35 // any prototype properties as needed 36 Sandbox.prototype = { 37   name: "My Application", 38   version: "1.0", 39   getName: function() { 40     return this.name; 41   } 42 };

代码清单8:实现Sandbox构造器

原文来自:Stoyan Stefanov – JavaScript Patterns Part 7:The Sandbox Pattern

更多资料:www.infoq.com/cn/articles/sandboxOnB

[转载]在 ASP.NET MVC 中创建自定义 HtmlHelper

mikel阅读(903)

[转载]在 ASP.NET MVC 中创建自定义 HtmlHelper – 雪的梦幻 – 博客园.

ASP.NET MVC应用程序的开发中,我们常碰到类似Html.Label或Html.TextBox这样的代码,它将在网页上产生一个label或input标记。 这些HtmlHelper的扩展方法有些像WebForm中的控件,只需传入一些参数即可生成相应的HTML代码。本文将介绍创建HtmlHelper的 方法。

Html.Textbox方法的返回值是MvcHtmlString,它生成了一些HTML代码。创建HtmlHelper,就像在生成HTML代 码。下面以一个带有简要描述功能的链接HtmlHelper为例介绍创建自定义HtmlHelper的方法。它将显示一个链接并在下方简要介绍该与该链接 内容有关的信息。在某些带有简要描述的项列表页上可能会需要类似的功能。最后的结果预览如下图。


上面使用了3次自定义的HtmlHelper,每次生成的代码如下

1 <div>
2 <p class=”LinkTitle”><a href=”#”>链接标题</a></p>
3 <p class=”LinkDescription”>链接描述</p>
4 </div>

可能稍微有点复杂,因为有了标签的嵌套。

做好准备工作以后,新建一个静态类LinkWithDescriptionExtensions,添加静态方法LinkWithDescription,代码如下

代码

1 /// <summary>
2 /// 带描述的链接扩展方法
3 /// </summary>
4 /// <param name=”htmlHelper”>要扩展的HtmlHelper类</param>
5 /// <param name=”title”>标题</param>
6 /// <param name=”url”>链接地址</param>
7 /// <param name=”description”>描述</param>
8 /// <returns>HTML代码</returns>
9 public static MvcHtmlString LinkWithDescription(this HtmlHelper htmlHelper, string title, string url, string description)
10 {
11 // 生成与标题链接有关的HTML代码
12 TagBuilder titleContainer = new TagBuilder(“p”);    // 标题链接容器p
13 TagBuilder titleLink = new TagBuilder(“a”);    // 标题中的文字要有链接,所以包含在a标签内
14 titleLink.MergeAttribute(“href”, url);    // 为a添加href属性并指定链接地址
15 titleLink.SetInnerText(title);    // 标题文字
16 titleContainer.InnerHtml = titleLink.ToString();    // 将a放到p中
17 titleContainer.AddCssClass(“LinkTitle”);    // 为标题添加样式
18
19 // 生成与链接描述有关的HTML代码
20 TagBuilder descriptionContainer = new TagBuilder(“p”);    // 连接描述容器p
21 descriptionContainer.InnerHtml = description;    // 描述文字
22 descriptionContainer.AddCssClass(“LinkDescription”);    // 为描述添加样式
23
24 // 将上述元素放入一个DIV中
25 TagBuilder div = new TagBuilder(“div”);
26 div.InnerHtml = string.Format(“{0}{1}”, titleContainer.ToString(), descriptionContainer.ToString());
27
28 // 返回生成的HTML代码
29 return MvcHtmlString.Create(div.ToString());
30 }

先理清结构再使用TagBuilder生成HTML代码是比较方便的。这里也可以使用拼接字符串的方式来生成HTML代码,只要最后得到所需的 HTML代码就可以,但是我个人不建议使用拼接字符串的方式,这样不仅思维容易混乱而且容易出错。使用TagBuilder既结构清晰,又不容易出错。

在ASPX页中,可以使用

1 <%:Html.LinkWithDescription(测试链接1, #, 这是测试链接1的描述)%>
2 <%:Html.LinkWithDescription(测试链接2, #, 这是测试链接2的描述)%>
3 <%:Html.LinkWithDescription(测试链接3, #, 这是测试链接3的描述)%>

来调用上面的扩展方法,这里的链接标题、链接地址和描述等信息可以通过ViewData等方式传入。在Action中写

代码

1 /// <summary>
2 /// Demo
3 /// </summary>
4 /// <returns>Demo视图</returns>
5 public ActionResult Demo()
6 {
7 // 创建链接信息列表
8 List<LinkInfo> links = new List<LinkInfo>();
9 links.Add(new LinkInfo { Description = 这是测试链接1的描述, Title = 测试链接1, Url = # });
10 links.Add(new LinkInfo { Description = 这是测试链接2的描述, Title = 测试链接2, Url = # });
11 links.Add(new LinkInfo { Description = 这是测试链接3的描述, Title = 测试链接3, Url = # });
12
13
14 ViewData[Links] = links;
15
16 return View();
17 }

在ASPX页中写

1 <%List<LinkInfo> links=ViewData[Links] as List<LinkInfo>;
2 foreach (var link in links)
3 { %>
4 <%:Html.LinkWithDescription(link.Title, link.Url, link.Description)%>
5 <%} %>

示例下载

本文适用于  ASP.NET MVC 2

[转载]获取网络MP3真实地址

mikel阅读(1045)

[转载]获取网络MP3真实地址 – Java综合 – Java – JavaEye论坛.

MP3网站的歌曲链接都采用了不同的加密方法,直接从页面的源文件中是找 不到其MP3的网址的。以下有两个public class都可独立运行,只要将其构造方法更名为main方法就可以了,同时还需要在给出的JAVA源代码中找到“//播放或下载代码…”这一行,将 其改为“Thread.sleep(200)”延时,否则同一IP频繁的连接请求会遭服务器拒绝或引发服务器的防恶意搜索保护。

1.获取百度新歌MP3真实地址

百度新歌的网址是http://xinge.baidu.com/index.html,打开该页面后用查看源文件,搜索“{sid:”,会看到这样的文本:

http://xinge.baidu.com/index.html的源文件片断
{sid:’468aecfeaabbd9467fb939a2e80da58a.mp3′,al:’今 生无缘’,ti:’一天爱一点’,si:’易欣 孙莺’,cp:’华友金信子 ‘,da:’2010-08-03′,cv:’38d9e957d5bdfb788989f1eb12239d8f.jpg’,lrc:’b118977f14893a70ab4652031dd1633d.txt’,dl:’511′,tl:’281394′,ico:0}

其中:“sid:”后面是歌曲连接、“al:”后是唱片集、“ti:”后面是歌曲标题、“si:”后面是歌手。

  • 读取“sid:”后面用一对单引号括起来的字符串,在前面加上 http://xinge.baidu.com/wgns/url/构成歌曲的链接,例如:http://xinge.baidu.com/wgns /url/468aecfeaabbd9467fb939a2e80da58a.mp3,这个链接是用于打开试听窗口的。
  • 试听窗口查看不到源文件,那就编程打开这个歌曲链接的网址并解析其源文件。接收到的源文件只有一行,在这一行字符串前面加上http://xinge.baidu.com就是MP3的真实网址了,这个网址可用于播放或下载MP3。

源代码如下:

Java代码
  1. /*
  2. * XingeBaidu.java – 获取’百度新歌’的MP3真实网址
  3. */
  4. package jmp123.player;
  5. import java.io.BufferedReader;
  6. import java.io.InputStream;
  7. import java.io.InputStreamReader;
  8. import java.net.URL;
  9. import java.net.HttpURLConnection;
  10. public class XingeBaidu {
  11. public XingeBaidu() {
  12. String strLine;
  13. int beginIndex, endIndex, idx;
  14. System.out.println(“连接到百度新歌\n”);
  15. try {
  16. URL url = new URL(“http://xinge.baidu.com/”);
  17. HttpURLConnection objHttp = (HttpURLConnection) url.openConnection();
  18. objHttp.setRequestProperty(“Connection”, “Keep-Alive”);
  19. InputStream objIS = objHttp.getInputStream();
  20. BufferedReader objReader = new BufferedReader(new InputStreamReader(objIS));
  21. while ((strLine = objReader.readLine()) != null) {
  22. if ((beginIndex = strLine.indexOf(“{sid:”)) != –1) {
  23. if ((idx = strLine.indexOf(“al:”)) != –1
  24. && (endIndex = strLine.indexOf(“‘,ti”)) != –1
  25. && idx + 4 < endIndex)
  26. System.out.printf(“[唱片集:%s]  “,strLine.substring(idx+4,endIndex));
  27. if ((idx = strLine.indexOf(“ti:”)) != –1
  28. && (endIndex = strLine.indexOf(“‘,si”)) != –1
  29. && idx + 4 < endIndex)
  30. System.out.printf(“%s”, strLine.substring(idx + 4,endIndex));
  31. if ((idx = strLine.indexOf(“si:”)) != –1
  32. && (endIndex = strLine.indexOf(“‘,cp”)) != –1
  33. && idx + 4 < endIndex)
  34. System.out.printf(”  [歌手:%s]”,strLine.substring(idx+4,endIndex));
  35. System.out.printf(“\n”);
  36. if ((endIndex = strLine.indexOf(“.mp3”)) != –1) {
  37. strLine = strLine.substring(beginIndex + 6, endIndex + 4);
  38. getMP3URL(“http://xinge.baidu.com/wgns/url/” + strLine);
  39. }
  40. }
  41. }
  42. catch (Exception e) {
  43. // e.printStackTrace();
  44. }
  45. }
  46. private void getMP3URL(String surl) throws Exception {
  47. String strLine;
  48. URL url = new URL(surl);
  49. HttpURLConnection objHttp = (HttpURLConnection) url.openConnection();
  50. InputStream objIS = objHttp.getInputStream();
  51. BufferedReader objReader = new BufferedReader(new InputStreamReader(objIS));
  52. if ((strLine = objReader.readLine()) != null) {
  53. strLine = “http://xinge.baidu.com” + strLine;
  54. System.out.println(strLine); //打印查找到的MP3的真实网址
  55. //播放或下载的代码…
  56. }
  57. objHttp.disconnect();
  58. objHttp = null;
  59. objReader.close();
  60. objReader = null;
  61. url = null;
  62. }
  63. }

2.获取搜狗MP3真实网址

用上面的方法不能获取搜狗新歌100等的MP3真实网址,原因可能是其服 务器有更严格的限制,防止用上面的方法去恶意搜索。前两天调试代码时连接上去,接收到的页面源文件中提示输入验证码,所以就不能用程序去解析其MP3网址 了,晕,接连两天都不行,不知道是前两天调试程序连接的太频繁了还是别的什么原因,触发了网站的防恶意搜索保护。

http://music.sogou.com/song/newtop_1.html源文件片断
<td width=”25″><a onclick=”window.open(‘http://mp3.sogou.com/down.so?t=%C0%F1%CE%EF&s=%BD%AD%D3%B3%C8%D8&w=02210600′,”,’width=428,height=394,scrollbars=no’);uigsPB(‘consume=phb_down’);return false;” href=”JavaScript:void(0);” target=”_blank” class=”link” title=”链接”></a></td>

源代码如下,自己对比一下,就能琢磨出与第一种方法有什么不同了。总的步骤是一样的,仍是两步:

  • 连接到http://music.sogou.com/song/newtop_1.html从接收到的数据流中查找到歌曲链接。打开这个页面的源文件,查找到window.open(,它后面用一对单引号括起来的内容就是歌曲的链接,这个链接是用于打开试听窗口的。
  • 用程序连接到这个歌曲链接,从接收到的数据流中查找以http://开头、以.mp3结尾的字符串,这个字符串就是MP3的真实网址。
Java代码
  1. /*
  2. * SogouNewTop.java – 获取’搜狗音乐新歌TOP100’的MP3真实网址
  3. */
  4. package jmp123.player;
  5. import java.io.IOException;
  6. import java.io.PrintWriter;
  7. import java.io.BufferedReader;
  8. import java.io.InputStreamReader;
  9. import java.net.Socket;
  10. /**
  11. * 创建、发送HTTP请求头
  12. */
  13. class MySocket {
  14. private String strReferer;
  15. private Socket socket;
  16. private PrintWriter pwOut;
  17. BufferedReader brIn;
  18. public MySocket(String strReferer) {
  19. this.strReferer = strReferer;
  20. }
  21. public void create(String surl) {
  22. int beginIndex, endIndex, iPort = 80;
  23. String strHost = surl.substring(7);
  24. endIndex = strHost.indexOf(“/”);
  25. String strPath = strHost.substring(endIndex);
  26. strHost = strHost.substring(0, endIndex);
  27. if( (beginIndex = strHost.indexOf(“:”)) != –1) {
  28. if(endIndex – beginIndex > 1)
  29. iPort = Integer.parseInt(strHost.substring(beginIndex+1, endIndex));
  30. strHost = strHost.substring(0, beginIndex);
  31. }
  32. try {
  33. socket = new Socket(strHost, iPort);
  34. pwOut = new PrintWriter(socket.getOutputStream(), true);
  35. // 构建HTTP请求头
  36. pwOut.println(“GET “ + strPath + ” HTTP/1.1″);
  37. pwOut.println(“Host:” + strHost);
  38. pwOut.println(“Referer:” + strReferer);
  39. pwOut.println(“Accept:*/*”);
  40. pwOut.println(“User-Agent:Mozilla/5.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322)”);
  41. pwOut.println(“Connection: Keep-Alive”);
  42. pwOut.println();
  43. // 调用socket.getInputStream方法时才发送HTTP请求头
  44. brIn = new BufferedReader(new InputStreamReader(socket.getInputStream()));
  45. catch (IOException e) {
  46. System.out.println(“创建套接字/输入流错误。”);
  47. System.exit(1);
  48. }
  49. }
  50. public BufferedReader getBufferedReader() {
  51. return brIn;
  52. }
  53. public void close() {
  54. try {
  55. brIn.close();
  56. pwOut.close();
  57. socket.close();
  58. catch (IOException e) {
  59. System.out.println(“关闭套接字错误。”);
  60. System.exit(1);
  61. }
  62. }
  63. }
  64. /**
  65. * 解析搜狗音乐新歌TOP100页面获取MP3真实网址。
  66. */
  67. public class SogouNewTop {
  68. private static final String strReferer = “http://music.sogou.com/song/newtop_1.html”;
  69. private MySocket htmlSocket = new MySocket(strReferer);
  70. private MySocket urlSocket = new MySocket(strReferer);
  71. /*
  72. * 查找页面的歌曲链接
  73. */
  74. public SogouNewTop() throws Exception {
  75. System.out.println(“连接到搜狗音乐新歌TOP100\n”);
  76. String strline = “”;
  77. htmlSocket.create(strReferer);
  78. BufferedReader brIn = htmlSocket.getBufferedReader();
  79. int beginIndex, endIndex;
  80. while ((strline = brIn.readLine()) != null) {
  81. // 1.查找歌曲名(可省略)
  82. if ((beginIndex = strline.indexOf(“consume=phb_song”)) != –1 ) {
  83. strline = strline.substring(beginIndex);
  84. if ((beginIndex = strline.indexOf(“>”)) != –1
  85. && (endIndex = strline.indexOf(“<“)) != –1) {
  86. strline = strline.substring(beginIndex+1, endIndex);
  87. System.out.println(“[歌曲名] “ + strline);
  88. }
  89. continue;
  90. }
  91. // 2.查找歌曲链接
  92. if ((beginIndex = strline.indexOf(“onclick=\”window.open(“)) != –1
  93. && (beginIndex = strline.indexOf(“http://mp3.sogou.com/down.so”)) != –1
  94. && (endIndex = strline.indexOf(“‘,”)) != –1) {
  95. strline = strline.substring(beginIndex, endIndex);
  96. getMP3URL(strline);
  97. }
  98. }
  99. htmlSocket.close();
  100. }
  101. /**
  102. * 分析歌曲链接找到其真实网址
  103. */
  104. private void getMP3URL(String surl) throws Exception {
  105. String strline = “”;
  106. urlSocket.create(surl);
  107. BufferedReader brIn = urlSocket.getBufferedReader();
  108. int beginIndex, endIndex;
  109. while ((strline = brIn.readLine()) != null) {
  110. if ((beginIndex = strline.indexOf(“http://”)) != –1
  111. && (endIndex = strline.indexOf(“.mp3”)) != –1) {
  112. strline = strline.substring(beginIndex, endIndex + 4);
  113. System.out.println(strline);    //打印MP3的真实地址
  114. //播放或下载的代码放这……;
  115. break;
  116. }
  117. }
  118. urlSocket.close();
  119. }
  120. }

另外,http://mp3.sogou.com/的:

  • 歌曲TOP100 http://music.sogou.com/song/topsong.html
  • 单曲抢先听 http://music.sogou.com/song/newnew_1.html
  • 劲爆dj音乐榜 http://music.sogou.com/song/newdj_1
  • 欧美流行榜 http://music.sogou.com/song/enpop_1.html

等页面的搜索方法跟上面的是一样的,并不建议你写代码到 http://music.sogou.com/song/目录下搜索*.html并用上述方法解析(就是实现传说中的网络蜘蛛的一部分功能),这样一顿海搜不礼貌哈,别人藏着掖着的就是不想让你查找到其MP3的网址的嘛。 这些页面上的歌曲经常更新,固定查找某一页面的MP3也可以听到新歌,不错了。一边干活一边听听歌,挺不错的哦~~


MP3网站的加密方法经常变更,到目前为止这种方法可用,不能保证一直可用。应用示例到我的主页http://jmp123.sf.net/ 下载最新的MP3播放程序,程序(jar包)用法见其中的readme.txt。