• 2007-08-15

    通过 HTTP POST 发送二进制数据

    Views: 37317 | No Comments

    我暂时还不清楚 HTTP 文件上传的具体细节, 但是我知道通过浏览器上传文件, 然后用 PHP 接收, 需要使用 PHP 的预定义变量 $_FILES. 最近我有一个应用需要使用 PHP 的预定义变量 $_POST 获取上传的文件(或者任何数据), 也就是通过 HTTP POST 参数传递二进制数据.

    工作在 ContentType = "application/x-www-form-urlencoded" 的模式下时, HTTP 协议使用 ASCII 字符集的一个子集来编码要传输的字节流. 如字符串 "a@" 如果以 ASCII 编码存在于内存中(以字节数组的形式), 也就是内存按字节读取为 HEX 是: 0x61 0x40, 那么该字节数组被传输时会被编码(URL 编码)成为 0x61 0x25 0x34 0x30, 被编码后字节数组如果按照 ASCII 编码显示, 就是 "a%40". 也就是说, 当你想传输 0x61 0x40 时, HTTP 传输的是 0x61 0x25 0x34 0x30.

    这种编码过程几乎所有的编程语言都提供支持, 在 C# 中你可以使用 HttpUtility.UrlEncode(), 输入一个字节数组, 返回一个 URL 编码的 字节数组.

    在 PHP 脚本中, 当你使用 $_POST['data'] 获取数组时, 该数组已经被 PHP 自动解码了. 按照上面的例子, $_POST['data'] 所指向的内存内容为 0x61 0x40 -- 但是, 默认情况下并不总是, 如果该数据包含单引号等少数几个字符, 那么它们的前面会被 PHP 加上 \.

    下面先给出 PHP 脚本, 保存一个通过 HTTP 参数传递的文件:

    <?php
        $filename = $_POST['filename'];
        $data = $_POST['data'];
        if(get_magic_quotes_gpc()){
            // 去掉斜杠
            $data = stripslashes($data);
        }
        file_put_contents($filename, $data);
    ?>
    

    C# 客户端为:

    using System;
    using System.Text;
    using System.Net;
    using System.Web;
    using System.IO;
    
    class Test
    {
        public static void Main(){
            try{
                WebResponse response;
                HttpWebRequest request;
                request = (HttpWebRequest)HttpWebRequest.Create("http://localhost/test.php");
                request.Method = "POST";
                request.ContentType = "application/x-www-form-urlencoded";
    
                String filename = "test.jpg";
                // HTTP 参数名
                byte[] keyBytes = Encoding.UTF8.GetBytes(String.Format("filename={0}&data=", filename));
    
                FileStream fs = new FileStream(filename, FileMode.Open);
                byte[] buffer = new byte[8192];
                int n = fs.Read(buffer, 0, buffer.Length);
                byte[] dataArray = new byte[n];
                Array.Copy(buffer, dataArray, n);
                dataArray = HttpUtility.UrlEncodeToBytes(dataArray);
    
                request.ContentLength = keyBytes.Length + dataArray.Length;
    
                Stream dataStream = request.GetRequestStream();
                // 发送参数名
                dataStream.Write(keyBytes, 0, keyBytes.Length);
                // 发送名为 data 对应的值.
                dataStream.Write(dataArray, 0, dataArray.Length);
                dataStream.Close();
    
                response = (HttpWebResponse)request.GetResponse();
                // 读取服务器的返回, 在本应用中, 如果正常, 返回空字符串.
                StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding("UTF-8"));
                Console.Write(sr.ReadToEnd());
    
                response.Close();
            } catch (Exception e) {
                Console.Write("Error: " + e.ToString() + "\r\n");
            }
        }
    
    }
    

    如果你要测试的文件小于 8K, 可以直接使用上面的程序测试. 如果大于, 你可以加大 buffer 的容量.

    上传成功后, 使用文件比较工具对比两个文件是否一致:

    diff test.jpg up_test.jpg
    

    这种方式的典型的应用是自定义文件上传, 你只需要更改少量的代码就可以实现支持断点续传功能的文件上传.

    Posted by ideawu at 2007-08-15 11:21:02
  • 2007-08-12

    PHP str_replace 函数的参数设计不合理之处

    Views: 18541 | 6 Comments

    PHP str_replace 函数的声明大概是这样: str_replace($search, $replace, $input[, &$count]), 比如在对一个字符串进行替换操作, $input 就是源字符串(称为数据源). 这很不合理, 因为它把数据源放在第3位, 而 str_pos, strtok, str_repeat 等等函数都是把数据源放在第1位.

    Posted by ideawu at 2007-08-12 15:42:52
  • 2007-07-24

    lighttpd速度是Apache的10倍

    Views: 25669 | 1 Comment

    今天试用了lighttpd, Windows预编译版, 体积只有5M左右. 我用我的博客程序试了下, PHP的执行速度竟然是Apache下的10倍! 正如其名--轻快. Apache大而全, 速度自然会受影响. 不过, 这个比较是不准确的, 因为我的Apache开了mod_rewrite, mod_vhost等等模块, 而lighttpd只开了mod_access和mod_cgi.

    Posted by ideawu at 2007-07-24 21:24:19 Tags: ,
  • 2007-07-16

    Spring + Hibernate Web 开发者笔记

    Views: 22688 | No Comments

    我将使用 Java 技术开发一个网上商店系统. 使用 Java 1.5, JSP 2.0, JSTL 1.1, Tomcat 5.5, MySQL 5.0, Spring Framework 2.0, Hibernate 3.2, XHTML 1.1, CSS 2.0 等技术, 有些地方会用到某些 JavaScript 库和 AJAX 技术.

    因为是开发笔记, 所以我不会详细介绍 Spring 和 Hibernate 的技术细节, 我只使用我需要的技术. 你可以参考 Spring 和 Hibernate 的书籍和官方网站.

    文档的访问网址为 http://www.ideawu.net/person/spring_hibernate_web_dev/

    欢迎你和我一起讨论. 访问我的网站 http://www.ideawu.net/, 在上面留言, 或者直接发 Email 给我, 发到 ideawu 在 163.com 的邮箱.

    Posted by ideawu at 2007-07-16 09:05:48
  • 2007-07-11

    Web开发中应该避免单向链接

    Views: 10597 | 1 Comment

    这里说的链接不仅仅是指HTML超链接, 还包括GET或者POST方法提交的表单等. 单向链接指的是下面的情况: 从页面A导航到页面B, 然后从页面B导航回页面A时, A页面已经失效.

    考虑下面的情况: 在一个博客的管理后台中, 有一个页面是文章查看页面, 如果在该文章查看页面提供了一个删除按钮, 该按钮通过超链接, 或者GET/POST表单转到执行删除的另一个页面, 那么, 当用户点击了删除按钮, 将无法返回原来的文章查看页面, 因为文章已经被删除了.

    最好的作法是不要在文章查看页面提供删除按钮, 只在文章的浏览(列表)页面显示删除按钮.

    你可能会认为, 即使在文章查看页面提供删除按钮也没有关系, 大可以在文章删除后总是跳转到文章的浏览页面. 问题是, 用户可能在浏览到第十页的时候删除了一篇文章, 你却不知道这个信息, 只能跳转到第一页. 这对用户是不友好的.

    Posted by ideawu at 2007-07-11 16:13:44
  • 2007-07-07

    获取焦点便选中所有文本的文本框是用户友好的吗?

    Views: 24352 | 6 Comments

    Web表单和桌面程序中经常要用到文本输入框, 比如登录框中的用户名和密码输入. 在Web表单中, 文本框获取焦点后(鼠标点击或者Tab键切换), 其中的文本并不会被选中. 但是有一些人会画蛇添足, 在文本框获取焦点后将其中的文本全选中, 如 PHPMyAdmin. 听到画蛇添足这个贬义词, 你就知道我是反对这种作法, 下面我要分析该作法的错误之处.

    例如, 当我在某个网站的登录框中输入 ideawu2, 然后点击登录. 事实上, 我的用户名是 ideawu, 只是一时笔误. 那么当我要修改时, 我当然是直接选中2后点击键盘上的Backspace键., 或者将光标定位到该文本框最后(这种操作方式比选中单个字符更容易), 然后点击键盘上的Backspace键. 如果你自作聪明地把所有的文本选中, 那么你是想让我把所有的文本删除再重新输入一遍吗? 即使是只更改一个字符?

    从理论上讲, 该作法的错误根源是: 事物的状态不是凭空产生的, 而是从一个状态到另一个状态, 而且后一个状态一般是以前一个状态为基础的改变.

    我有理由相信这种愚蠢的作法源于Windows操作系统的文本框的默认行为, 这种文本框最重要地应用是资源浏览器和IE的地址栏. 在Linux操作系统下的Gnome中的文本框就不会有这种行为. 所以, 当我在Windows下使用浏览器浏览网页, 想在地址栏中输入一个新的网址时, 比如, 当前我正在浏览 http://www.ideawu.net/, 因为某种原因, 页面上并没有提供到 http://www.ideawu.net/ideablog/ 的链接, 最简便的作法是把光标定位在网址的末尾, 然后输入 ideablog 回车. 但是, 在Windows下我根本无法这样做! 无论我点住鼠标同时把鼠标上, 下,  左(至少会选中一个字符), 右移动, 都无法把光标定位在网址的末尾, 这让我很苦恼.

    奇怪的是, Windows的IE使用这种文本框的默认行为, 而Firefox的Windows版本的地址栏为什么也使用这种默认行为?

    我常想, 微软是不是应该改变Windows文本框的这个愚蠢的默认行为? Web开发者能不能放弃这个愚蠢的作法?

    Posted by ideawu at 2007-07-07 11:55:36 Tags:
|<<<151617181920212223>>>| 19/28 Pages, 163 Results.