细心!用心!耐心!

吾非文人,乃市井一俗人也,读百卷书,跨江河千里,故申城一游; 一两滴辛酸,三四年学业,五六点粗墨,七八笔买卖,九十道人情。

BlogJava 联系 聚合 管理
  1 Posts :: 196 Stories :: 10 Comments :: 0 Trackbacks

[精华] jsp中下载程序问题


http://www.chinaunix.net 作者:huangmw  发表于:2002-12-24 11:18:22
发表评论】【查看原文】【Java讨论区】【关闭

我下载了jspsmartupload类,然后编写了下载程序,如下:
<%@page contentType=&quot;text/html;charset=gb2312&quot;%>;<%@page language=&quot;java&quot; import=&quot;com.jspsmart.upload.*&quot;%>;<jsp:useBean id=&quot;mySmartUpload&quot; scope=&quot;page&quot; class=&quot;com.jspsmart.upload.SmartUpload&quot;/>;<%mySmartUpload.initialize(pageContext);
mySmartUpload.downloadFile(&quot;d:\\111.txt&quot;);
%>;
运行后,我txt文件中的内容就直接显示在了页面上,zip文件也同样,只不过是乱码,有什么办法不显示而和一般的下载一样呢?



 eclipse 回复于:2002-10-18 10:54:22

应该是SmartUpload类的问题,JSP程序没有问题,我感觉


 huangmw 回复于:2002-10-18 11:27:17

这个类是从www.jspsmart.com站点下载的,不会他们编的类问题吧?
你能提供一个类似这种类给我吗?谢谢了


 eclipse 回复于:2002-10-18 12:11:30

TestFileDownload.JSP页面的例子:

<%
// 得到文件名字和路径
String filename = ”MengxianhuiDocTest.doc”;
String filepath = ”D:\\”;

// 设置响应头和下载保存的文件名
response.setContentType(”APPLICATION/OCTET-STREAM”);
response.setHeader(”Content-Disposition”,
”attachment; filename=\”” + filename + ”\””);

// 打开指定文件的流信息
java.io.FileInputStream fileInputStream =
new java.io.FileInputStream(filepath + filename);

// 写出流信息
int i;
while ((i=fileInputStream.read()) != -1) {
out.write(i);
}
fileInputStream.close();
out.close();
%>;

值得注意的是:在你要下载的文件内容里,除了文件的内容之外,不应该再附加有其它任何的字符,包括空格和回车换行符。我们有时在编写代码的时候,为了使代码清晰可读,往往会添加一些空格、制表符或者回车换行符,这样虽然看起来比较清晰,但有时可能会得不到正确的结果。比如:
<%@ page import=”java.io.*”
%>; <jsp:useBean id=”MyBeanFromMengxianhui” scope=”page”
class=”com.Mengxianhui.DownloadBean” />;
应该写成这样:
<%@ page import=”java.io.*”
%>;<jsp:useBean id=”MyBeanFromMengxianhui” scope=”page”
class=”com.Mengxianhui.DownloadBean” />;


 eclipse 回复于:2002-10-18 12:13:21

请注意:APPLICATION/OCTET-STREAM是设置下载类型

要改成你实际的类型,如excel要写成:application/vnd.ms-excel



 eclipse 回复于:2002-10-18 12:17:39

如果不用jspsmart,你就需要了解浏览器端的编码方式,在传到服务器端时你才能解码。也才可以得到上传文件的相关信息。看下面的代码。
package mshtang.fileUpload;
import java.io.*;
/**一个存放文件信息的类,包括文件的名称(String),
**字段名(String), Content-Type(String)和内容(byte[])
**还提供了一个直接将文件内容保存到一个文件的函数 void saveTo(File f)
**可以调用 类{@link ContentFactory}中的适当方法,生成该类的实例。
** @see ContentFactory
** @see ContentFactory#getFileParameter
** @see ContentFactory#getFileParameterValues
**/

public class FileHolder
{
    String contentType;
    byte[] buffer;
    String fileName;
    String parameterName;

    FileHolder(byte[] buffer, String contentType, String fileName, String parameterName)
    {
        this.buffer = buffer;
        this.contentType = contentType;
        this.fileName = fileName;
        this.parameterName = parameterName;
    }
    /**把文件的内容存到指定的文件中,
    **<b>;这个方法不会检查这个文件是否可写、是否已经存在。</b>;
    **@param file  目的文件
    **@throws 在 I/O 操作中被抛出的 IOException
    **/
    public void saveTo(File file) throws IOException
    {
        BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));
        out.write(buffer);
        out.close();
    }

    /**把文件的内容存到指定的文件中,
    **<b>;这个方法不会检查这个文件是否可写、是否已经存在。</b>;
    **@param name 目的文件名
    **@throws 在 I/O 操作中被抛出的 IOException
    **/
    public void saveTo(String name) throws IOException
    {
        saveTo(new File(name));
    }

   /**
    **返回一个文件内容的字节数组
    **@return 一个代表文件内容的字节数组
   **/
    public byte[] getBytes()
    {
        return buffer;
    }

   /**
    **返回该文件在文件上载前在客户端的名称
    **@return 该文件在文件上载前在客户端的名称
   **/
    public String getFileName()
    {
        return fileName;
    }

   /**
    **返回该文件的 Content-Type
    **@return 该文件的 Content-Type
   **/
    public String getContentType()
    {
        return contentType;
    }

   /**
    **返回上载该文件时,Html 页面窗体中 file 控件的 name 属性
    **@return 返回上载该文件时,Html 页面窗体中 file 控件的 name 属性
   **/
    public String getParameterName()
    {
        return parameterName;
    }
}


 eclipse 回复于:2002-10-18 12:22:31

存放报文内容的类:

package mshtang.fileUpload;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;

/**存放报文内容的类,提供类似于 ServletRequest 中的部分 get 方法
*你必须在 html 页面的窗体(form)中指定 enctype=&quot;multipart/form-data&quot;。
*才可以正确的使用这个类。
**/
public class ContentFactory
{
    private Hashtable values;        //存放name=value,其中value存放在另一个类中
    private Hashtable files;         //存放文件内容的。
    private ContentFactory(Hashtable values, Hashtable files)
    {
        this.values=values;
        this.files=files;
    }
    public String getParameter(String name)
    {
        Vector v = (Vector)values.get(name);
        if( v != null)
        {
          return (String)v.elementAt(0);
        }
        return null;
    }
    public Enumeration getParameterNames()
    {
        return values.keys();
    }
    public String[] getParameterValues(String name)
    {
        Vector v = (Vector)values.get(name);
        if(v != null)
        {
          String[] result = new String[v.size()];
          v.toArray(result);
          return result;
        }
        return new String[0];
    }

   /**
     *返回一个 FileHolder 实例,该实例包含了通过字段名为name的file控件上载的文件信息,
     *如果不存在这个字段或者提交页面时,没有选择上载的文件,则返回 null。
     * <p>;如果 Html 页面中存在不止一个字段名为name的file控件,
     * 返回值等于{@link #getFileParameterValues}中的第一个元素。
     *
     * @param name:一个<code>;String</code>;,对应于Html页面窗体中file控件
     *的name 属性。
     *
     * @return返回:一个 FileHolder 实例,该实例包含了通过字段名为 name 的 file 控件上载的文件信息,
     *如果不存在这个字段或者提交页面是,没有选择上载的文件,则返回 null。
     *
     * @see         #getFileParameterValues
     *
     */
    public FileHolder getFileParameter(String name)
    {
        Vector v = (Vector)files.get(name);
        if(v != null)
        {
          return (FileHolder)v.elementAt(0);
        }
        return null;
    }
    /**
     * 返回一个 由 String 对象构成的 Enumeration ,包含了 Html 页面
     *窗体中所有 file 控件的 name 属性。
     *如果窗体中没有 file 控件,则返回一个空的 Enumeration
     * @return             返回一个 由 String 对象构成的 Enumeration ,包含了 Html 页面
     *窗体中所有 file 控件的 name 属性。
     *如果窗体中没有 file 控件,则返回一个空的 Enumeration
     */
    public Enumeration getFileParameterNames()
    {
        return files.keys();
    }
   /**
     *返回一个 FileHolder 数组,该数组包含了所有通过字段名为 name 的 file 控件上载的文件信息,
     *如果不存在这个字段或者提交页面时,没有选择任何上载的文件,则返回一个 零元素的数组(不是 null )。
     * @param name     一个 <code>;String</code>; ,对应于 Html 页面窗体中 file 控件
     *的name 属性。
     *
     * @return        返回一个 FileHolder 数组,该数组包含了所有通过字段名为 name 的 file 控件上载的文件信息,
     *如果不存在这个字段或者提交页面时,没有选择任何上载的文件,则返回一个 零元素的数组(不是 null )。
     *
     * @see         #getFileParameter
     */
    public FileHolder[] getFileParameterValues(String name)
    {
        Vector v=(Vector)files.get(name);
        if(v!=null)
        {
            FileHolder[] result=new FileHolder[v.size()];
            v.toArray(result);
            return result;
        }
        return new FileHolder[0];
    }
    //------------->;Factory 部分
    /**
    **返回根据当前请求生成的一个 ContentFactory 实例
    **@param request 提交的请求
    **@return 返回根据当前请求生成的一个 ContentFactory 实例,如果 request
    数据包的内容不是以 mutilpart/form-data 型编码,则返回 null。
    **@throws ContentFactoryException 当提交的数据和文件太大时抛出,
    **根据 Content-Length 判断,默认的许可值为 1024* 1024。
    **/
    public static ContentFactory getContentFactory(HttpServletRequest request) throws ContentFactoryException,IOException
    {
        // default maxLength is 1MB.
        return getContentFactory(request, 1024*1024);
    }
        /**
    **返回根据当前请求生成的一个 ContentFactory 实例
    **@param request 提交的请求
    **@param maxLength 数据包的最大长度,默认为1024*1024
    **@return 返回根据当前请求生成的一个 ContentFactory 实例,如果 request
    数据包的内容不是以 mutilpart/form-data 型编码,则返回 null。
    **@throws ContentFactoryException 当提交的数据和文件太大时抛出,
    **根据 Content-Length 判断,默认的许可值为 1024* 1024。
    **/
    public static ContentFactory getContentFactory(HttpServletRequest request, int maxLength) throws ContentFactoryException, IOException
    {
      Hashtable values = new Hashtable();
      Hashtable files = new Hashtable();
      String contentType = request.getContentType();
      int contentLength = request.getContentLength();
      if (contentLength >; maxLength)
      {
        ContentFactoryException e=new ContentFactoryException(&quot;上传数据太多,请不要选择太大的文件&quot;);
        throw e;
      }
      if(contentType == null || !contentType.startsWith(&quot;multipart/form-data&quot;))
      {
        return null;
      }
//get out the boudary from content-type
      int start = contentType.indexOf(&quot;boundary=&quot;);
//这里应该
      int boundaryLen = new String(&quot;boundary=&quot;).length();
      String boundary = contentType.substring(start + boundaryLen);
      boundary = &quot;--&quot; + boundary;
//用字节表示,以免 String  和 byte 数组的长度不一致
      boundaryLen = bytesLen(boundary);
//把request 中的数据读入一个byte数组
      byte buffer[] = new byte[contentLength];
      int once = 0;
      int total = 0;
      DataInputStream in = new DataInputStream(request.getInputStream());
      while((total < contentLength) &amp;&amp; (once >;= 0))
      {
        once = in.read(buffer, total, contentLength);
        total += once;
      }
//对buffer中的数据进行拆分
      int pos1 = 0;                  //pos1 记录 在buffer 中下一个 boundary 的位置
//pos0,pos1 用于 subBytes 的两个参数
      int pos0 = byteIndexOf(buffer, boundary, 0);//pos0 记录 boundary 的第一个字节在buffer 中的位置
      do
      {
        pos0 += boundaryLen;                                 //记录boundary后面第一个字节的下标
        pos1 = byteIndexOf(buffer, boundary, pos0);
        if(pos1==-1)
        {
          break;
        }//
        pos0 += 2;//考虑到boundary后面的 \r\n
        parse(subBytes(buffer, pos0, pos1-2), values, files);      //考虑到boundary后面的\r\n
        pos0=pos1;
      }while(true);
      return new ContentFactory(values,files);
    }

private static void parse(byte[] buffer, Hashtable values, Hashtable files)
    {
            /* this is a smiple to parse
            [boundary]
            Content-Disposition: form-data; name=&quot;file3&quot;; filename=&quot;C:\Autoexec.bat&quot;
            Content-Type: application/octet-stream

            @echo off
            prompt $d $t [ $p ]$_$$

            [boundary]
            Content-Disposition: form-data; name=&quot;Submit&quot;

            Submit
            [boundary]
            */
        String[] tokens={&quot;name=\&quot;&quot;,&quot;\&quot;; filename=\&quot;&quot;, &quot;\&quot;\r\n&quot;,&quot;Content-Type: &quot;,&quot;\r\n\r\n&quot;};
           //                          0           1                               2          3                         4
        int[] position=new int[tokens.length];

        for (int i=0;i<tokens.length ;i++ )
        {
            position=byteIndexOf(buffer,tokens,0);
        }
        if (position[1]>;0 &amp;&amp; position[1]<position[2])
        {
            //包含tokens 中的第二个元素,说明是个文件数据段
            //1.得到字段名
            String name =subBytesString(buffer,position[0]+bytesLen(tokens[0]),position[1]);
            //2.得到文件名
            String file= subBytesString(buffer,position[1]+bytesLen(tokens[1]),position[2]);
            if (file.equals(&quot;&quot;)) return;
            file=new File(file).getName();     //this is the way to get the name from a path string
            //3.得到 Content-Type
            String contentType=subBytesString(buffer,position[3]+bytesLen(tokens[3]),position[4]);
           //4.得到文件内容
            byte[] b=subBytes(buffer,position[4]+bytesLen(tokens[4]),buffer.length);
            FileHolder f=new FileHolder(b,contentType,file,name);
            Vector v=(Vector)files.get(name);
            if (v==null)
            {
                v=new Vector();
            }
            if (!v.contains(f))
            {
                v.add(f);
            }
            files.put(name,v);
            //同时将 name 属性和 file 属性作为普通字段,存入values;
            v=(Vector)values.get(name);
            if (v==null)
            {
                v=new Vector();
            }
            if (!v.contains(file))
            {
                v.add(file);
            }
            values.put(name,v);
        }else
        {
//            String[] tokens={&quot;name=\&quot;&quot;,&quot;\&quot;; filename=\&quot;&quot;, &quot;\&quot;\r\n&quot;,&quot;Content-Type: &quot;,&quot;\r\n\r\n&quot;}
//             index                      0           1                               2          3                         4
            //不包含tokens 中的第二个元素,说明是个 name/value 型的数据段
            //所以没有tokens[1]和 tokens[3]
            //name 在 tokens[0] 和 tokens[2] 之间
            //value 在 tokens[4]之后
            //1.得到name
            String name =subBytesString(buffer,position[0]+bytesLen(tokens[0]),position[2]);
            String value= subBytesString(buffer,position[4]+bytesLen(tokens[4]),buffer.length);
            Vector v=(Vector)values.get(name);
            if (v==null)
            {
                v=new Vector();
            }
            if (!v.contains(value))
            {
                v.add(value);
            }
            values.put(name,v);
        }
    }
   /**字节数组中的 indexof 函数,与 String 类中的 indexOf类似
    **@para source 源字节数组
    **@para search 目标字符串
    **@para start 搜索的起始点
    **@return 如果找到,返回search的第一个字节在buffer中的下标,没有则返回-1
    **/
    private static int byteIndexOf (byte[] source,String search,int start)
    {
        return byteIndexOf(source,search.getBytes(),start);
    }

   /**字节数组中的 indexof 函数,与 String 类中的 indexOf类似
    **@para source 源字节数组
    **@para search 目标字节数组
    **@para start 搜索的起始点
    **@return 如果找到,返回search的第一个字节在buffer中的下标,没有则返回-1
    **/
    private static int byteIndexOf (byte[] source,byte[] search,int start)
    {
        int i;
        if (search.length==0)
        {
            return 0;
        }
        int max=source.length-search.length;
        if (max<0)
            return -1;
        if (start>;max)
            return -1;
        if (start<0)
            start=0;
    // 在source中找到search的第一个元素
    searchForFirst:
        for (i=start;i<=max ; i++)
        {
            if (source==search[0])
            {
                //找到了search中的第一个元素后,比较剩余的部分是否相等
                int k=1;
                while(k<search.length)
                {
                    if (source[k+i]!=search[k])
                    {
                        continue searchForFirst;
                    }
                    k++;
                }
                return i;
            }
        }
        return -1;
    }
    /**
    **用于从一个字节数组中提取一个字节数组
    **类似于 String 类的substring()
    **/
    private static byte[] subBytes(byte[] source,int from,int end)
    {
        byte[] result=new byte[end-from];
        System.arraycopy(source,from,result,0,end-from);
        return result;
    }
    /**
    **用于从一个字节数组中提取一个字符串
    **类似于 String 类的substring()
    **/
    private static String subBytesString(byte[] source,int from,int end)
    {
        return new String(subBytes(source,from,end));
    }
    /**
    **返回字符串S转换为字节数组后的长度
    **/
    private static int bytesLen(String s)
    {
        return s.getBytes().length;
    }
}


 huangmw 回复于:2002-10-18 16:13:53

好长的一篇代码,只有慢慢看了,不过还是谢谢


 eclipse 回复于:2002-10-18 16:19:10

最后边两篇有兴趣了看,没兴趣了就不要看了,是我扩展的

三四五等几贴我想可以解决你的问题了,不过不是smart。

其实你可以将下载的smart反编译,看看具体代码,或者重新下载试试



 huangmw 回复于:2002-10-18 16:26:19

呵呵,好的,谢谢,不过怎么反编译呢?啊。。。不要打我。。。菜鸟无罪


 eclipse 回复于:2002-10-18 16:39:21

谈谈JAVA的反编译 
 
谈谈JAVA的反编译
于瑶
如今JAVA语言在全世界范围正如火如荼般的流行,它广范地应用在INTERNET的数据库、多媒体、CGI、及动态网页的制作方面。1999年在美国对JAVA程序员的需求量首次超过C++!
作者因最近分析一些JAVA程序,对JAVA的反编译进行了一番了解,下面将我所了解的情况作以下介绍,希望对JAVA爱好者有所帮助。
JAVA是采用一种称做“字节编码”的程序结构,分为小程序(嵌入到HTML文件中)和应用程序(直接在命令状态下执行)两种类型。无论哪种结构,一旦用JAVAC 命令编译后,均变成后缀为CLASS的同名可执行文件。这种文件是不可阅读的代码。
经查阅了SUN公司的JDK(JDK1.1.3)文档资料后,我找到了一个据称是可反编译JAVA的JAVAP文件(EXE),这个文件位于\JDK\BIN\ 下面,经按说明使用后,感到失望,原来这个“反编译”仅可反编译出JAVA程序的数据区(定义)、若干方法和类的引用等。
这里我用了一个简单例子来说明问题。
JAVA的源程序hello_java.java如下:

import java.applet.*;
import java.awt.*;

public class hello_java extends Applet
{
public void paint(Graphics g)
{
g.drawString(&quot;Hello Java!\n&quot;,20,20);
}


经用反编译命令:javap -c -package -public -private hello_java hello.java 
得到的反编译结果(hello.java)如下:(有关javap命令的选择参数请见其使用说明,这里-c表示选择了反编译)

Compiled from hello_java.java
public synchronized class hello_java extends java.applet.Applet 
/* ACC_SUPER bit set */
{
public void paint(java.awt.Graphics);
public hello_java();

Method void paint(java.awt.Graphics)
0 aload_1
1 ldc #1 <String &quot;Hello Java!
&quot;>;
3 bipush 20
5 bipush 20
7 invokevirtual #6 <Method java.awt.Graphics.drawString(Ljava/lang/String;II)V>;
10 return

Method hello_java()
0 aload_0
1 invokespecial #5 <Method java.applet.Applet.<init>;()V>;
4 return
}

从上述结果不难看出该反编译未能将源程序全译出来,像语句g.drawString(&quot;Hello Java!\n&quot;,20,20); 就没有。随着程序量增加,未能编译的JAVA语句还会更多。所以这个反编译程序仅能起个参考作用。
幸亏有了INTERNET,笔者通过YAHOO很快找到了一个JAVA反编译“自由软件”(SHAREWARE),http://www.inter.nl.net/users/H.P.van.Vliet/mocha.htm 。 这个软件叫MOCHA,据说是一位30来岁的加拿大的研究生所完成,仅是个“?”版,原因是这位叫做H.P.VAN.VLIET的小伙子患癌逝世了,十分可惜呀!
经使用MOCHA反编译软件,感到这个软件十分好用,笔者试反编译多个JAVA程序,均得到很好的结果。
这里给出如何使用这个软件,首先,用WINZIP等将&quot;mocha-b1.zip&quot; 解开得到&quot;mocha.zip&quot;文件,&quot;mocha.zip&quot;不须再解开,这个包内包括了反编译的类文件,只需将其拷贝到JDK所在的目录下,如:c:\jdk\bin\ 此外,须设置路径:SET CLASSPATH=c:\myclasses;c:\jdk\bin\mocha.zip
MOCHA用法:
java mocha.Decompiler [-v] [-o] Class1.class Class2.class ...
&quot;java&quot; 调用Java虚拟机 
&quot;mocha.Decompiler&quot; 指示要进行JAVA反编译
&quot;-v&quot; 选择详细输出
&quot;-o&quot; 选写入已有的.mocha 文件
&quot;ClassX.class&quot; 指出要反编译类名
注意,不需给出输出的JAVA文件名,因为MOCHA自动产生一个与CLASS同名但扩展名为MOCHA的JAVA源文件。
对于上例,可用命令:
java mocha.Decompiler [-v] [-o] hello_java.class
得到的源文件:
/* Decompiled by Mocha from hello_java.class */
/* Originally compiled from hello_java.java */

import java.applet.Applet;
import java.awt.Graphics;

public synchronized class hello_java extends Applet
{
public void paint(Graphics g)
{
g.drawString(&quot;Hello Java!\n&quot;, 20, 20);
}

public hello_java()
{
}
}
我们不难发现,此文件与编译前的JAVA源文件完全一样!笔者曾经用MOCHA反编译出最大为80K的源文件,均取得成功。
在此,笔者向英年早逝的VLIET表示敬意,感谢他给我们留下这个工具软件。
如读者下载MOCHA有困难,可给笔者来电子邮件,笔者可将MOCHA寄去。
posted on 2007-07-16 23:23 张金鹏 阅读(713) 评论(0)  编辑  收藏

只有注册用户登录后才能发表评论。


网站导航: