notice
2011年12月16日 | 分类: IT技术 | 标签:

关于JAVA如何正确的处理静态文件,数据流的断点续传,查阅许多资料后,还是在tomcat中找到了灵感,具体请参考org.apache.catalina.servlets.DefaultServlet

简单说明下,关于tomcat处理资源请求的主要包括:
1 处理流程在 protected void serveResource(HttpServletRequest request,HttpServletResponse response,boolean content) 中
2 使用缓存进行处理 CacheEntry cacheEntry = resources.lookupCache(path);
3 解析Range的正确方式请参考 // Parse range specifier
765 ranges = parseRange(request, response, cacheEntry.attributes);
766
767 // ETag header
768 response.setHeader("ETag", cacheEntry.attributes.getETag());
769
770 // Last-Modified header
771 response.setHeader("Last-Modified",
772 cacheEntry.attributes.getLastModifiedHttp());
773
774 // Get content length
775 contentLength = cacheEntry.attributes.getContentLength();
776 // Special case for zero length files, which would cause a
777 // (silent) ISE when setting the output buffer size
778 if (contentLength == 0L) {
779 content = false;
780 }

这个方法才是重点 protected Range parseContentRange(HttpServletRequest request,HttpServletResponse response)

经测试,完美支持CHROME,IE,FF,ANDROID,IPAD,IPHONE,SAFARI。
大家可以参考来处理静态资源或是数据流
下面的是整合后的代码,有删改

 public String readFile(){
 
	   long start = System.currentTimeMillis();
	   //open virtual dir file in cloud 
	   mongodbfile cloudStore = new mongodbfile();
	   BufferedOutputStream bos = null;
	   String fileId = "";
	   getResponse().reset();
	   getResponse().resetBuffer();
	   int bufferSize = 1024;
	   getResponse().setBufferSize(bufferSize);
	   try {
		 getResponse().setContentType("image/");//请相应修改
		 getResponse().setHeader("Accept-Ranges", "bytes");
		   log.debug("readCloudStoreFile fileId = {}",fileId);
		   int readResult = cloudStore.ReadBegin(fileId);
		   if(readResult == 0){//文件读取成功
			   long contentLength = cloudStore.GetFileLength(fileId);
                           //模拟Etag的信息
			   getResponse().setHeader("Etag", "W/\""+contentLength+"-1316052976000\"");
			   getResponse().setHeader("Last-Modified", "Thu, 15 Sep 2011 02:16:16 GMT");
			   byte[] buffer = new byte[bufferSize];
			   bos = new BufferedOutputStream(getResponse().getOutputStream());
			   long pos = 0;			   
			   ArrayList ranges = null;			   
			   long s = System.currentTimeMillis();
			   // Parse range specifier
                           ranges = parseRange(request, response, contentLength);
                           long requestLength = 0;			 
               if ( (((ranges == null) || (ranges.isEmpty()))
                               && (request.getHeader("Range") == null) )
                       || (ranges == FULL) ){
 
            	   if (contentLength < Integer.MAX_VALUE) {
    				   getResponse().setContentLength((int) contentLength);
                   } else {
                       // Set the content-length as String to be able to use a long
                	   getResponse().setHeader("content-length", "" + contentLength);
                   }
            	   requestLength = contentLength;
               }else{
		// 若客户端传来Range,说明之前下载了一部分,设置206状态(SC_PARTIAL_CONTENT)
				   getResponse().setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
 
				   log.debug("=======ranges.size() {}",ranges.size());
				   if (ranges.size() == 1) {
 
		                Range range = (Range) ranges.get(0);
		                response.addHeader("Content-Range", "bytes "
		                                   + range.start
		                                   + "-" + range.end + "/"
		                                   + range.length);
		                log.debug("Content-Range:  {}","bytes "
                                + range.start
                                + "-" + range.end + "/"
                                + range.length);
 
//重点在这里对实时请求的数据长度和数据流剩余长度,和写回Response数据长度的判断
		                long length = range.end - range.start + 1;
		                if (length < Integer.MAX_VALUE) {
		                    response.setContentLength((int) length);
		                } else {
		                    // Set the content-length as String to be able to use a long
		                    response.setHeader("content-length", "" + length);
		                }
		                log.debug("request range length = {}",length);    
		                pos = range.start;
		                requestLength = length;
		            } else {
                                //这里未处理range数组的情况
		                response.setContentType("multipart/byteranges; boundary="
		                                        + mimeSeparation);
	                    log.warn("@@@@@@@@@@@ for multipart transfer todo");
		            }   
			   }
               log.debug("###########skip pos = {}",pos);
			   if (pos != 0) {
				   // 略过已经传输过的字节
				   long sk = System.currentTimeMillis();
				   cloudStore.Skip(pos);				  
			   }
			   long ws = System.currentTimeMillis();
			   if(requestLength < bufferSize){
				   bufferSize = (int)requestLength;
			   }
 
			   int readedLen = 0;
			   for(long len = 0;len<requestLength;){
				   readedLen = cloudStore.Read(buffer, bufferSize);
				   bos.write(buffer,0,readedLen);
				   len+=readedLen;
			   }
 
			   bos.flush();
			   log.debug("write data spent {}",System.currentTimeMillis()-ws);
		   }
 
		   log.debug("write to response file success fileId = {}",fileId);
	   } catch (Exception e) {
 
	   }finally{
		   //关闭资源	   
	   return null;
   }
 
        /**
	 * MIME multipart separation string
	 */
	protected static final String mimeSeparation = "CATALINA_MIME_BOUNDARY";
 
   /**
    * Full range marker.
    */
   protected static ArrayList FULL = new ArrayList();
 
   /**
    * Parse the range header.
    *
    * @param request The servlet request we are processing
    * @param response The servlet response we are creating
    * @return Vector of ranges
    */
   protected ArrayList parseRange(HttpServletRequest request,
                               HttpServletResponse response,long length)
       throws IOException {
 
       // Checking If-Range
       String headerValue = request.getHeader("If-Range");
       log.debug("headerValue = ",headerValue);
       if (headerValue != null) {
 
           long headerValueTime = (-1L);
           try {
               headerValueTime = request.getDateHeader("If-Range");
           } catch (IllegalArgumentException e) {
               ;
           }
 
           String eTag = "W/\""+length+"-1316052976000\"";
           long lastModified = 1316052976000L;
 
           log.debug("headerValueTime = ",headerValueTime);
           if (headerValueTime == (-1L)) {
 
               // If the ETag the client gave does not match the entity
               // etag, then the entire entity is returned.
               if (!eTag.equals(headerValue.trim())){
            	   log.debug("If the ETag the client gave does not match the entity etag, "
                            +"then the entire entity is returned.");
            	   return FULL;
               }
           } else {
 
               // If the timestamp of the entity the client got is older than
               // the last modification date of the entity, the entire entity
               // is returned.
               if (lastModified > (headerValueTime + 1000)){
            	   log.debug("If the timestamp of the entity the client got is older "
            +"than the last modification date of the entity, the entire entity is returned");
            	   return FULL;
 
               }
           }
 
       }
 
       long fileLength = length;
 
       if (fileLength == 0)
           return null;
 
       // Retrieving the range header (if any is specified
       String rangeHeader = request.getHeader("Range");
 
       log.debug("rangeHeader = ",rangeHeader);
       if (rangeHeader == null){
    	   return null;
       }    
       // bytes is the only range unit supported (and I don't see the point
       // of adding new ones).
       if (!rangeHeader.startsWith("bytes")) {
           response.addHeader("Content-Range", "bytes */" + fileLength);
           response.sendError
               (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
           return null;
       }
 
       rangeHeader = rangeHeader.substring(6);
 
       // Vector which will contain all the ranges which are successfully
       // parsed.
       ArrayList<range> result = new ArrayList</range><range>();
       StringTokenizer commaTokenizer = new StringTokenizer(rangeHeader, ",");
 
       // Parsing the range list
       while (commaTokenizer.hasMoreTokens()) {
           String rangeDefinition = commaTokenizer.nextToken().trim();
 
           Range currentRange = new Range();
           currentRange.length = fileLength;
 
           int dashPos = rangeDefinition.indexOf('-');
 
           if (dashPos == -1) {
               response.addHeader("Content-Range", "bytes */" + fileLength);
               response.sendError
                   (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
               return null;
           }
 
           if (dashPos == 0) {
 
               try {
                   long offset = Long.parseLong(rangeDefinition);
                   currentRange.start = fileLength + offset;
                   currentRange.end = fileLength - 1;
               } catch (NumberFormatException e) {
                   response.addHeader("Content-Range",
                                      "bytes */" + fileLength);
                   response.sendError
                       (HttpServletResponse
                        .SC_REQUESTED_RANGE_NOT_SATISFIABLE);
                   return null;
               }
 
           } else {
 
               try {
                   currentRange.start = Long.parseLong
                       (rangeDefinition.substring(0, dashPos));
                   if (dashPos < rangeDefinition.length() - 1)
                       currentRange.end = Long.parseLong
                           (rangeDefinition.substring
                            (dashPos + 1, rangeDefinition.length()));
                   else
                       currentRange.end = fileLength - 1;
               } catch (NumberFormatException e) {
                   response.addHeader("Content-Range",
                                      "bytes */" + fileLength);
                   response.sendError
                       (HttpServletResponse
                        .SC_REQUESTED_RANGE_NOT_SATISFIABLE);
                   return null;
               }
 
           }
 
           if (!currentRange.validate()) {
               response.addHeader("Content-Range", "bytes */" + fileLength);
               response.sendError
                   (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
               return null;
           }
 
           result.add(currentRange);
       }
 
       return result;
   }
 
   // ------------------------------------------------------ Range Inner Class
 
 
   protected class Range {
 
       public long start;
       public long end;
       public long length;
 
       /**
        * Validate range.
        */
       public boolean validate() {
           if (end >= length)
               end = length - 1;
           return ( (start >= 0) && (end >= 0) && (start < = end)
                    && (length > 0) );
       }
 
       public void recycle() {
           start = 0;
           end = 0;
           length = 0;
       }
 
   }
</range>
没有评论 (74 views)
2011年9月3日 | 分类: IT技术 | 标签: ,

提取自yoyoplayer的MP3解析器可以读取MP3文件的信息,并利用百度获取MP3文件的歌词地址
java -jar mp3.jar -n “d:/KwDownload/song/刘德华-母亲.mp3″

输出:

使用方法:

MP3Info mp3 = new MP3Info(“测试读取MP3″,”d:/KwDownload/song/刘德华-母亲.mp3″,-1,true);
mp3.reRead();

mp3.isValid():true

======================================
歌手mp3.getArtist() 刘德华
歌名mp3.getTitle() 母亲
专辑mp3.getAlbum()
格式化名称mp3.getFormattedDisplayName() 刘德华 – 母亲
格式化名称mp3.getFormattedName()刘德华 – 母亲
格式化参数mp3.getFormat()mp3 44kHz 192kbps
声道mp3.getChannelInfo()立体声
备注mp3.getComment()
类型mp3.getType()mp3
年份mp3.getYear()
采样率mp3.getSampled()44kHz
时长mp3.getFormattedLength()03:42
时长long型mp3.getLength()222

======================================

===============搜索歌词文件===========
%C1%F5%B5%C2%BB%AA+%C4%B8%C7%D7
size:1
歌曲:母亲
歌手:刘德华
下载地址:http://www.51lrcgc.com/asp/lrc.asp?id=20090917g9W6fF

try http://download.csdn.net/source/3571532

1 条评论 (58 views)
2011年8月11日 | 分类: IT技术 | 标签:
private final String QUERY_TABLE_SPACE_SQL = "select nvl(b.tablespace_name,nvl(a.tablespace_name,'UNKNOWN')) name," +
			" Mbytes_alloc-nvl(Mbytes_free,0)    ,  nvl(Mbytes_free,0)   , nvl(Maxfree_ext,0) , " +
			"  ((Mbytes_alloc-nvl(Mbytes_free,0))/Mbytes_alloc)*100  pct_used from (select sum(bytes)/1024/1024   Mbytes_free  ," +
			"MAX(BYTES)/1024/1024 Maxfree_ext ,tablespace_name from   dba_free_space group by tablespace_name)   a ," +
			" (select  sum(bytes)/1024/1024 Mbytes_alloc ,  tablespace_name from  dba_data_files group by tablespace_name)   b" +
			" where a.tablespace_name (+) = b.tablespace_name and upper(nvl(b.tablespace_name,nvl(a.tablespace_name,'UNKNOWN'))) " +
			"in (:tableSpaceNames) order by name";
没有评论 (17 views)
2011年8月10日 | 分类: IT技术 | 标签: ,
JsTree Demo
Version: jstree pre 1.0 stable
Add a demo for jstree in demo/index.html
Integrate the usage of metadata, click the node event ,attrs,toggle nodes and ajax json in jstree
Changed apple theme and classic theme background fixed for in ie6.

1.

<script type="text/javascript" src="./_lib/jquery.js"></script>
<script type="text/javascript" src="./jquery.jstree.js"></script>

2.

<script type="text/javascript" class="source below">
$(function () {
	$.ajaxSetup({cache:false});//ajax request don't use the cache
$("#jsonDemo").jstree({ 
		// List of active plugins
		"plugins" : [ 
			"themes","json_data", "ui"
		],
		// I usually configure the plugin that handles the data first
		// This example uses JSON as it is most common
		"json_data" : { 
			"data" : [{"attr":{"id":"1204","isLast":"false","name":"A Node"},
				          "data":"A Node",
				         "metadata":{"id":"1204","isLast":"false","name":"A Node"},
				          "state":"closed"},
			          {"attr":{"id":"1205","isLast":"true","name":"B Node"},
			          	"data":"B Node",
			          	"metadata":{"id":"1205","isLast":"true","name":"B Node"},
			          	"state":"close"}
			         ],
			"ajax" : { "url" : "./_demo/_tree_json.json",
						     "data": function (n){
	                            return{
 	                                //set the url request param,multi param separate by ,
			                            "parentId" : n.attr ? n.attr("id") : "null",
			                            "name":  n.attr ? n.attr("name") : "null"
	                            };
                        }
			 }
		},	
	   "themes" : {
	         "theme" : "classic", //apple,default,if in ie6 recommented you use classic
	         "dots" : true,
	         "icons" : true
	   }
	})
	.bind("select_node.jstree",function(event,data){  
			if("true" == data.rslt.obj.attr("isLast")){ 
				 //get the attrs data you set in the attrs field;
			    alert(data.rslt.obj.attr("id")+data.rslt.obj.attr("isLast"));
			    //you can do something here...
			}else{
				//toggle node refer to the id setted in the metadata
				//get the metadata id field value : jQuery.data(data.rslt.obj[0], "id");
				//The metadata id value should be different to each other !!!
				//otherwise, the toggle_node will work incorrect !!!
				$("#jsonDemo").jstree("toggle_node","#"+jQuery.data(data.rslt.obj[0], "id"));
			}    
    })
    // prevent the default event of the link 
	.delegate("a", "click", function (event, data) { event.preventDefault(); })
	;
});
</script>
没有评论 (64 views)
2011年4月25日 | 分类: IT技术 | 标签:

Question: Metro for SOAP web services, In the response, the namespace prefixes are ns2, ns3, ns4, and so on. I’d like to be able to instruct the web services stack to use custom names instead.

To solve this, please do as follow:
1 In the response/request bean package,named a package-info.java
2 set the annotation like

@javax.xml.bind.annotation.XmlSchema(xmlns = {
@XmlNs(prefix = "fixml",
namespaceURI= "http://www.fixprotocol.org/FIXML-5-0-SP2")},
elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED
)
package org.fixprotocol.fixml_5_0_sp2;
import javax.xml.bind.annotation.XmlNs;

keyword:

prefix = "fixml",
namespaceURI= "http://www.fixprotocol.org/FIXML-5-0-SP2"

you can replace this keyword as you want.
the result is

<soapenv:envelope xmlns:soapenv=”http://schemas.xmlsoap.org/soap/envelope/”  xmlns:fixml=”http://www.fixprotocol.org/FIXML-5-0-SP2″>
</soapenv:envelope>

<soapenv:envelope xmlns:soapenv=”http://schemas.xmlsoap.org/soap/envelope/”  xmlns:fixml=”http://www.fixprotocol.org/FIXML-5-0-SP2″></soapenv:envelope>

according the Metro Schema Validation guide http://metro.java.net/guide/Schema_Validation.html
we can implement the request and response soap message scheme validation.

没有评论 (31 views)
Page 1 of 1512345...10...Last »