Zhiqim Httpd即知启蒙WEB容器,是Zhiqim Framework面向WEB开发的多例服务,提供更简洁配置、积木式组件模块和天然的模型模板设计。
HttpOutputStream.java6KB
/*
* 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙,邂逅框架梦]
*
* https://zhiqim.org/project/zhiqim_framework/zhiqim_httpd.htm
*
* Zhiqim Httpd is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package org.zhiqim.httpd;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.zhiqim.kernel.util.Ints;
import org.zhiqim.kernel.util.Zips;
/**
* HTTP阻塞式输入流,通过ByteArrayOutputStream实现header和content分隔
*
* @version v1.0.0 @author zouzhigang 2014-3-21 新建与整理
*/
public class HttpOutputStream extends OutputStream implements HttpdConstants
{
private final HttpOutputStreamWrap output;
private int chunkSize = _MAX_CHUNKED_SIZE_;
private HttpSenderImpl sender;
private ByteArrayOutputStream content;
private boolean chunked;
private boolean headed;
public HttpOutputStream(HttpConnection conn)
{
this.output = new HttpOutputStreamWrap(conn);
this.content = new ByteArrayOutputStream();
}
public void setSender(HttpSenderImpl sender)
{
this.sender = sender;
}
public void setChunkSize(int chunkSize)
{
if (chunkSize < 4 * _KiB_)
this.chunkSize = 4 * _KiB_;
else if (chunkSize > _MAX_CHUNKED_SIZE_)
this.chunkSize = _MAX_CHUNKED_SIZE_;
else
this.chunkSize = chunkSize;
}
/*************************************************************************************/
//继承和实现的方法
/*************************************************************************************/
public void write(int b) throws IOException
{
if (sender.isCommitted())
return;
if (content.size() >= chunkSize)
{//上次最后满一次分块
flush();
}
content.write(b);
}
public void write(byte[] b, int off, int len) throws IOException
{
if (sender.isCommitted())
return;
if (content.size() >= chunkSize)
{//上次最后满一次分块
flush();
}
if (content.size() + len <= chunkSize)
{//不足或刚好一次分块,直接添加即可
content.write(b, off, len);
return;
}
//超过分块数
int page = (content.size() + len - 1) / chunkSize + 1;
//先写第一块,并刷入
int firstLen = chunkSize - content.size();
content.write(b, off, firstLen);
flush();
//后续块
int length = firstLen;
for (int i=1;i<page;i++)
{
int count = (i<page-1)?chunkSize:len-length;
content.write(b, off+length, count);
length += count;
if (count >= chunkSize)
{//满即刷入
flush();
}
}
}
public void flush() throws IOException
{
if (!headed)
{
output.write(sender.buildChunkedHeader(true));
headed = true;
}
writeChunked();
}
/*************************************************************************************/
//内部调用的方法
/*************************************************************************************/
/** 重置内容,如前面有写入缓冲,但未提交发现错误 */
public void reset()
{
content.reset();
}
/** 提交流 */
public void commit() throws IOException
{
if (!chunked)
{//未分块,写入头和内容
output.write(sender.buildChunkedHeader(false));
content.writeTo(output);
output.flush();
}
else
{//分块,写入最后块和结尾
if (content.size() > 0){
writeChunked();
}
writeChunkedEnd();
}
}
/** 关闭流 */
public void finished() throws IOException
{
if (content != null)
{
content.close();
content = null;
}
output.close();
}
/*************************************************************************************/
//整包写入
/*************************************************************************************/
public boolean isChunked()
{
return chunked;
}
public boolean processGZipCompress()
{
try
{
content = Zips.gzip(content);
return true;
}
catch (Exception ex)
{
return false;
}
}
/** 获取写入的长度 */
public long getOutputLength()
{
return output.length();
}
public int getContentLength()
{
return content.size();
}
/*************************************************************************************/
//写入分块数据
/*************************************************************************************/
private void writeChunked() throws IOException
{
output.write(Ints.toBytesHex(content.size()));
output.write(_CRLF_);
content.writeTo(output);
output.write(_CRLF_);
output.flush();
content.reset();
chunked = true;
}
private void writeChunkedEnd() throws IOException
{
output.write(_0_);
output.write(_CRLF_);
output.write(_CRLF_);
output.flush();
}
}