Zhiqim Httpd即知启蒙WEB容器,是Zhiqim Framework面向WEB开发的多例服务,提供更简洁配置、积木式组件模块和天然的模型模板设计。
HttpUploader.java8KB
/*
* 版权所有 (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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import org.zhiqim.kernel.util.Files;
import org.zhiqim.kernel.util.Streams;
import org.zhiqim.kernel.util.Strings;
import org.zhiqim.kernel.util.Validates;
/**
* HTTP 上传类
*
* @version v1.0.0 @author zouzhigang 2014-3-21 新建与整理
*/
public class HttpUploader implements HttpdConstants, AutoCloseable
{
private static final String CONTENT_DISPOSITION = "Content-Disposition:";
private static final String CONTENT_TYPE = "Content-Type:";
private HashMap<String, String> parameters;
private String name;
private String fileContentType;
private String fileName;
private ByteArrayOutputStream fileContent;
public HttpUploader()
{
parameters = new HashMap<String, String>();
fileContent = new ByteArrayOutputStream();
}
public String getParameter(String name)
{
return parameters.get(name);
}
public HashMap<String, String> getParameters()
{
return parameters;
}
public String getName()
{
return name;
}
public String getFileName()
{
return fileName;
}
public String getFileExt()
{
return Files.getFileExt(fileName);
}
public String getFileContentType()
{
return fileContentType;
}
public byte[] getBytes()
{
return fileContent.toByteArray();
}
public int getFileSize()
{
return fileContent.size();
}
public void writeTo(OutputStream out) throws IOException
{
fileContent.writeTo(out);
}
public void writeToFile(String filePath) throws IOException
{
Files.write(filePath, fileContent.toByteArray());
}
public InputStream getInputStream()
{
return new ByteArrayInputStream(fileContent.toByteArray());
}
public void close()
{
try{fileContent.close();}catch (IOException e){}
fileContent = null;
}
public void parseHttpRequest(HttpRequest request, int maxSize) throws Exception
{
int contentLength = request.getContentLength();
if (contentLength == 0)
throw new Exception("非法的文件上传信息[contentLength == 0]");
if (contentLength > maxSize)
throw new Exception("文件超过大小[contentLength == "+ contentLength +"]");
String contentType = request.getContentType();
if (contentType == null)
throw new Exception("非法的文件上传信息[contentType == null]");
if (!contentType.startsWith(_MULTIPART_FORM_DATA_))
throw new Exception("非法的文件上传信息[contentType == "+ contentType +"]");
int ind = contentType.indexOf("boundary=");
if (ind == -1)
throw new Exception("非法的文件上传信息[contentType == "+ contentType +"]");
String boundary = contentType.substring(ind+"boundary=".length());
byte[] buffer = new byte[4096];
InputStream input = request.getInputStream();
int len = 0; String name = null;String curContentType = null;
int type = 0; boolean isSkinEmptyLine = false;boolean isLastBR = false;
while ((len = Streams.readLine(input, buffer, buffer.length)) != -1)
{
String line = new String(buffer, 0, len, request.getCharacterEncoding());
if (line.startsWith("--"+boundary+"--"))
{//全部内容结束
break;
}
if (line.startsWith("--"+boundary))
{//新内容开始
type = 0; //重置为未知
isSkinEmptyLine = false;//重置下一行可以跳过空行
continue;
}
if (!isSkinEmptyLine && Validates.isEmptyBlank(line))
{//跳过内容之前的空行
isSkinEmptyLine = true;
continue;
}
if (Strings.startsWithIgnoreCase(line, CONTENT_DISPOSITION))
{//内容配置开始,前面空行不处理
String formData = line.substring(CONTENT_DISPOSITION.length()).trim();
name = parseFormDataName(formData);
if (name == null)
{//无效的配置
isSkinEmptyLine = false;//重置下一行可以跳过空行
continue;
}
String filename = parseFormDataFileName(formData);
if (filename == null)
{
type = 1;//文本类型
if (!parameters.containsKey(name))
parameters.put(name, null);
}
else
{
type = 2;//文件类型
this.name = name;
this.fileName = filename;
}
continue;
}
if (Strings.startsWithIgnoreCase(line, CONTENT_TYPE))
{//内容类型开始,文本类型不一般会指定
curContentType = line.substring(CONTENT_TYPE.length()).trim();
if (type == 2)
this.fileContentType = curContentType;
isSkinEmptyLine = false;//重置下一行可以跳过空行
continue;
}
if (type == 1)
{//文本参数,仅支持一行,多行表示该参数多个值,空行不处理
parseFormValue(request, name, line);
isSkinEmptyLine = false;//重置下一行可以跳过空行
}
else if (type == 2)
{//文件内容
if (isLastBR)
{//上一个回车换行加上,如果不能到这里,表示上一个回车换行是结尾
fileContent.write(_CRLF_);
isLastBR = false;
}
if (!line.endsWith(_BR_))
fileContent.write(buffer, 0, len);
else
{//文件如果遇到\r\n,要下一个循环判断是否是结尾
fileContent.write(buffer, 0, len-2);
isLastBR = true;
}
}
else
{//未定位到文件还是文本时不处理,一般是多余的\r\n
}
}
}
/** 解析form-data */
private String parseFormDataName(String formData)
{
String key = "name=\"";
int ind = formData.indexOf(key);
if (ind == -1)
return null;
int ind2 = formData.indexOf("\"", ind+key.length());
if (ind2 == -1)
return null;
return formData.substring(ind+key.length(), ind2);
}
/** 解析form-data */
private String parseFormDataFileName(String formData)
{
String key = "filename=\"";
int ind = formData.indexOf(key);
if (ind == -1)
return null;
int ind2 = formData.indexOf("\"", ind+key.length());
if (ind2 == -1)
return null;
String name = formData.substring(ind+key.length(), ind2);
return name;
}
private void parseFormValue(HttpRequest request, String name, String line)
{
line = line.replaceAll("\r", "");
line = line.replaceAll("\n", "");
String value = parameters.get(name);
if (value == null)
{
parameters.put(name, line);
request.setParameterOnCNT(name, line);
}
else
{
parameters.put(name, value + "," + line);//参数名相同时,使用逗号隔开
request.setParameterOnCNT(name, value + "," + line);
}
}
}