Some progress on fileupload

This commit is contained in:
Ziver Koc 2016-07-13 17:22:11 +02:00
parent f2939f819f
commit 5606f57514
17 changed files with 193 additions and 116 deletions

View file

@ -26,6 +26,7 @@
<JAVADOC />
<SOURCES>
<root url="file://$MODULE_DIR$/lib" />
<root url="jar://$USER_HOME$/.ideaLibSources/commons-fileupload-1.2.1-sources.jar!/" />
</SOURCES>
<jarDirectory url="file://$MODULE_DIR$/lib" recursive="false" />
<jarDirectory url="file://$MODULE_DIR$/lib" recursive="false" type="SOURCES" />

Binary file not shown.

BIN
lib/commons-io-2.5.jar Executable file

Binary file not shown.

View file

@ -41,7 +41,7 @@ public class IOUtil {
* @param stream
* @return the stream contents
*/
public static byte[] getContent(InputStream stream) throws IOException{
public static byte[] readContent(InputStream stream) throws IOException{
DynamicByteArrayStream dyn_buff = new DynamicByteArrayStream();
byte[] buff = new byte[8192];
int len = 0;
@ -60,8 +60,8 @@ public class IOUtil {
* @param stream
* @return a String with the content of the stream
*/
public static String getContentAsString(InputStream stream) throws IOException{
return getContentAsString(new InputStreamReader(stream));
public static String readContentAsString(InputStream stream) throws IOException{
return readContentAsString(new InputStreamReader(stream));
}
/**
@ -71,7 +71,7 @@ public class IOUtil {
* @param reader
* @return a String with the content of the stream
*/
public static String getContentAsString(Reader reader) throws IOException{
public static String readContentAsString(Reader reader) throws IOException{
StringBuilder str = new StringBuilder();
BufferedReader in = null;
if(reader instanceof BufferedReader)
@ -89,7 +89,8 @@ public class IOUtil {
}
/**
* Reads on line terminated by a new line or carriage return from a stream.
* Reads one line terminated by a new line or carriage return from a stream.
* Will only read ASCII based char streams.
*
* @param in the stream to read from
* @return a String that contains one line excluding line terminating
@ -106,6 +107,26 @@ public class IOUtil {
return null; // End of the stream
return str.toString();
}
/**
* Reads one line terminated by a new line or carriage return from a Reader.
* Will only read ASCII based char streams.
*
* @param in the Reader to read from
* @return a String that contains one line excluding line terminating
* characters, null if it is the end of the stream
*/
public static String readLine(Reader in) throws IOException {
StringBuilder str = new StringBuilder(80);
int c = 0;
while ((c=in.read()) >= 0 && (c != '\n') && (c != '\r'))
str.append((char)c);
if (c == '\r')
in.read(); // if the last char is carriage return we assume the next char in the stream will be new line so skip it
if (c == -1 && str.length() == 0)
return null; // End of the stream
return str.toString();
}
/**
* Copies all data from one InputStream to another OutputStream.

View file

@ -163,7 +163,7 @@ public class FileUtil {
}
public static byte[] getByteContent(File file) throws IOException {
InputStream in = new FileInputStream(file);
byte[] data = IOUtil.getContent(in);
byte[] data = IOUtil.readContent(in);
in.close();
return data;
}
@ -176,7 +176,7 @@ public class FileUtil {
*/
public static String getContent(URL url) throws IOException{
InputStream in = url.openStream();
String data = new String(IOUtil.getContent(in));
String data = new String(IOUtil.readContent(in));
in.close();
return data;
}

4
src/zutil/net/FTPClient.java Normal file → Executable file
View file

@ -175,7 +175,7 @@ public class FTPClient extends Thread{
BufferedInputStream data_in = getDataInputStream();
sendCommand("NLST "+path);
String data = new String(IOUtil.getContent(data_in));
String data = new String(IOUtil.readContent(data_in));
data_in.close();
readCommand();
@ -194,7 +194,7 @@ public class FTPClient extends Thread{
BufferedInputStream data_in = getDataInputStream();
sendCommand("LIST "+path);
String data = new String(IOUtil.getContent(data_in));
String data = new String(IOUtil.readContent(data_in));
data_in.close();
readCommand();

View file

@ -26,6 +26,7 @@ package zutil.net.http;
import zutil.net.http.HttpPrintStream.HttpMessageType;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
@ -52,8 +53,8 @@ public class HttpClient implements AutoCloseable{
private String data;
// Response variables
private HttpHeaderParser rspHeader;
private BufferedReader rspReader;
private HttpHeaderParser rspHeader;
private BufferedInputStream rspReader;
@ -128,7 +129,7 @@ public class HttpClient implements AutoCloseable{
// Response
if(rspHeader != null || rspReader != null) // Close previous request
this.close();
rspReader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
rspReader = new BufferedInputStream(conn.getInputStream());
rspHeader = new HttpHeaderParser( rspReader );
return rspHeader;
@ -137,7 +138,7 @@ public class HttpClient implements AutoCloseable{
public HttpHeaderParser getResponseHeader(){
return rspHeader;
}
public BufferedReader getResponseReader(){
public BufferedInputStream getResponseReader(){
return rspReader;
}

View file

@ -26,6 +26,8 @@ package zutil.net.http;
import zutil.converter.Converter;
import java.io.BufferedReader;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
@ -37,6 +39,7 @@ public class HttpHeader {
private HashMap<String, String> urlAttributes;
private float version;
private int httpCode;
private InputStream in;
// Parameters
private HashMap<String, String> headers;
@ -51,13 +54,13 @@ public class HttpHeader {
/**
* @return true if this header represents a server response
* @return true if this header represents a server response
*/
public boolean isResponse(){
return !request;
}
/**
* @return true if this header represents a client request
* @return true if this header represents a client request
*/
public boolean isRequest(){
return request;
@ -103,47 +106,47 @@ public class HttpHeader {
return null;
}
/**
* @return a Iterator with all defined url keys
* @return a Iterator with all defined url keys
*/
public Iterator<String> getURLAttributeKeys(){
return urlAttributes.keySet().iterator();
}
/**
* Returns the URL attribute value of the given name.
*
* returns null if there is no such attribute
* @return the URL attribute value of the given name. null if there is no such attribute
*/
public String getURLAttribute(String name){
return urlAttributes.get( name );
}
/**
* @return a Iterator with all defined headers
* @return a Iterator with all defined headers
*/
public Iterator<String> getHeaderKeys(){
return headers.keySet().iterator();
}
/**
* Returns the HTTP attribute value of the given name.
*
* returns null if there is no such attribute
* @return the HTTP attribute value of the given name. null if there is no such attribute
*/
public String getHeader(String name){
return headers.get( name.toUpperCase() );
}
/**
* @return a Iterator with all defined cookies
* @return a Iterator with all defined cookies
*/
public Iterator<String> getCookieKeys(){
return cookies.keySet().iterator();
}
/**
* Returns the cookie value of the given name.
*
* returns null if there is no such attribute
* @return the cookie value of the given name. null if there is no such attribute.
*/
public String getCookie(String name){
return cookies.get( name );
}
/**
* @return a Reader that contains the body of the http request.
*/
public InputStream getInputStream(){
return in;
}
protected void setIsRequest(boolean request) { this.request = request; }
@ -159,6 +162,9 @@ public class HttpHeader {
protected void setRequestURL(String url){
this.url = url.trim().replaceAll("//", "/");
}
protected void setInputStream(InputStream in){
this.in = in;
}
protected HashMap<String,String> getHeaderMap(){
return headers;

View file

@ -24,17 +24,18 @@
package zutil.net.http;
import com.mysql.jdbc.Buffer;
import zutil.StringUtil;
import zutil.io.IOUtil;
import zutil.io.StringInputStream;
import zutil.parser.URLDecoder;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.io.*;
import java.util.Map;
import java.util.regex.Pattern;
public class HttpHeaderParser {
public static final String HEADER_COOKIE = "COOKIE";
private static final String HEADER_COOKIE = "COOKIE";
private static final Pattern PATTERN_COLON = Pattern.compile(":");
private static final Pattern PATTERN_EQUAL = Pattern.compile("=");
@ -42,7 +43,7 @@ public class HttpHeaderParser {
private static final Pattern PATTERN_SEMICOLON = Pattern.compile(";");
private BufferedReader in;
private InputStream in;
private boolean readStatusLine;
@ -51,8 +52,8 @@ public class HttpHeaderParser {
*
* @param in is the stream
*/
public HttpHeaderParser(BufferedReader in){
this.in = in;
public HttpHeaderParser(InputStream in){
this.in = in;
this.readStatusLine = true;
}
@ -62,28 +63,29 @@ public class HttpHeaderParser {
* @param in is the String
*/
public HttpHeaderParser(String in){
this(new BufferedReader(new StringReader(in)));
this(new StringInputStream(in));
}
public HttpHeader read() throws IOException {
HttpHeader header = new HttpHeader();
String line = null;
String line;
// First line
if (readStatusLine) {
if( (line=in.readLine()) != null && !line.isEmpty() )
if( (line= IOUtil.readLine(in)) != null && !line.isEmpty() )
parseStatusLine(header, line);
else
return null;
}
// Read header body
while( (line=in.readLine()) != null && !line.isEmpty() ){
parseHeaderLine(header, line);
while( (line=IOUtil.readLine(in)) != null && !line.isEmpty() ){
parseHeaderLine(header.getHeaderMap(), line);
}
// Post processing
parseHeaderValue(header.getCookieMap(), header.getHeader(HEADER_COOKIE));
header.setInputStream(in);
return header;
}
@ -119,7 +121,7 @@ public class HttpHeaderParser {
if(index > -1){
header.setRequestURL( statusLine.substring(0, index));
statusLine = statusLine.substring( index+1, statusLine.length());
parseURLParameters(header, statusLine);
parseURLParameters(header.getUrlAttributeMap(), statusLine);
}
else{
header.setRequestURL(statusLine);
@ -128,14 +130,16 @@ public class HttpHeaderParser {
}
/**
* Parses a http key value paired header line
* Parses a http key value paired header line.
* Note that all header keys will be stored with
* uppercase notation to make them case insensitive.
*
* @param header the header object where the cookies will be stored.
* @param map a map where the header key(Uppercase) and value will be stored.
* @param line is the next line in the header
*/
public static void parseHeaderLine(HttpHeader header, String line){
public static void parseHeaderLine(Map<String,String> map, String line){
String[] data = PATTERN_COLON.split( line, 2 );
header.getHeaderMap().put(
map.put(
data[0].trim().toUpperCase(), // Key
(data.length>1 ? data[1] : "").trim()); //Value
}
@ -160,20 +164,30 @@ public class HttpHeaderParser {
}
}
/**
* Parses a string with variables from a get or post request that was sent from a client
*
* @param header the header object where the cookies will be stored.
* @param header the header object where the url attributes key and value will be stored.
* @param urlAttributes is the String containing all the attributes
*/
public static void parseURLParameters(HttpHeader header, String urlAttributes){
parseURLParameters(header.getUrlAttributeMap(), urlAttributes);
}
/**
* Parses a string with variables from a get or post request that was sent from a client
*
* @param map a map where the url attributes key and value will be stored.
* @param urlAttributes is the String containing all the attributes
*/
public static void parseURLParameters(Map<String,String> map, String urlAttributes){
String[] tmp;
urlAttributes = URLDecoder.decode(urlAttributes);
// get the variables
String[] data = PATTERN_AND.split( urlAttributes );
for(String element : data){
tmp = PATTERN_EQUAL.split(element, 2);
header.getUrlAttributeMap().put(
map.put(
tmp[0].trim(), // Key
(tmp.length>1 ? tmp[1] : "").trim()); //Value
}

View file

@ -29,10 +29,7 @@ import zutil.log.LogUtil;
import zutil.net.threaded.ThreadedTCPNetworkServer;
import zutil.net.threaded.ThreadedTCPNetworkServerThread;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.*;
import java.net.Socket;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@ -151,12 +148,12 @@ public class HttpServer extends ThreadedTCPNetworkServer{
*/
protected class HttpServerThread implements ThreadedTCPNetworkServerThread{
private HttpPrintStream out;
private BufferedReader in;
private BufferedInputStream in;
private Socket socket;
public HttpServerThread(Socket socket) throws IOException{
out = new HttpPrintStream(socket.getOutputStream());
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
in = new BufferedInputStream(socket.getInputStream());
this.socket = socket;
}
@ -175,30 +172,19 @@ public class HttpServer extends ThreadedTCPNetworkServer{
String tmp = null;
//******* Read in the post data if available
if (header.getHeader("Content-Length") != null) {
if (header.getHeader("Content-Length") != null &&
header.getHeader("Content-Type") != null &&
header.getHeader("Content-Type").contains("application/x-www-form-urlencoded")) {
// Reads the post data size
tmp = header.getHeader("Content-Length");
int post_data_length = Integer.parseInt(tmp);
int post_data_length = Integer.parseInt(header.getHeader("Content-Length"));
// read the data
StringBuilder tmpBuff = new StringBuilder();
// read the data
for (int i = 0; i < post_data_length; i++) {
tmpBuff.append((char) in.read());
}
tmp = header.getHeader("Content-Type");
if (tmp.contains("application/x-www-form-urlencoded")) {
// get the variables
HttpHeaderParser.parseURLParameters(header, tmpBuff.toString());
} else if (tmp.contains("application/soap+xml") ||
tmp.contains("text/xml") ||
tmp.contains("text/plain")) {
// save the variables
header.getUrlAttributeMap().put("", tmpBuff.toString());
} else if (tmp.contains("multipart/form-data")) {
// TODO: File upload
throw new UnsupportedOperationException("HTTP Content-Type 'multipart-form-data' not supported.");
}
// get the variables
HttpHeaderParser.parseURLParameters(header, tmpBuff.toString());
}
//**************************** HANDLE REQUEST *********************************

View file

@ -25,10 +25,13 @@
package zutil.net.http.multipart;
import zutil.io.IOUtil;
import zutil.log.LogUtil;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import static zutil.net.http.multipart.MultipartParser.HEADER_CONTENT_TYPE;
/**
@ -37,16 +40,23 @@ import java.util.Map;
* @author Ziver
*/
public class MultipartFileField implements MultipartField{
private static final Logger logger = LogUtil.getLogger();
private String fieldname;
private String filename;
private String contentType;
private byte[] content;
private InputStream in;
protected MultipartFileField(String name, String filename, String contentType, BufferedReader in) throws IOException {
this.fieldname = name;
this.filename = filename;
this.contentType = contentType;
protected MultipartFileField(Map<String,String> headers, InputStream in) throws IOException {
this.fieldname = headers.get("name");
this.filename = headers.get("filename");
this.contentType = headers.get(HEADER_CONTENT_TYPE);
this.in = in;
if (contentType != null && !contentType.equalsIgnoreCase("application/octet-stream"))
logger.warning("Unsupported Content-Type: "+contentType);
}
/**
@ -71,19 +81,42 @@ public class MultipartFileField implements MultipartField{
return contentType;
}
public InputStream getInputStream(){
return in;
/**
* First time this method is called the contents of the
* file will be read into a byte array and returned.
* Subsequent calls will just return the array without
* reading any more data from the stream.
*
* Note: Only one of the methods {@link #getContent()} or
* {@link #saveToFile(File)} can be used as they will consume the data in the stream.
*
* @return a byte array containing the file data. null if the Stream has already been consumed
*/
public byte[] getContent() throws IOException {
if (in != null) {
content = IOUtil.readContent(in);
in = null; // reset InputStream
}
return content;
}
/**
* Reads in all data and save it into the specified file
*
/**
* Reads in all data and save it into the specified file.
*
* Note: Only one of the methods {@link #getContent()} or
* {@link #saveToFile(File)} can be used as they will consume the data in the stream.
*
* @param file is the new file where the data will be stored
*/
public void saveToFile(File file) throws IOException {
if (in == null)
throw new IOException("Stream already consumed.");
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));
IOUtil.copyStream(in, out);
out.close();
in = null; // reset InputStream
}
}

View file

@ -49,8 +49,8 @@ import java.util.logging.Logger;
*/
public class MultipartParser implements Iterable<MultipartField>{
private static final Logger logger = LogUtil.getLogger();
private static final String HEADER_CONTENT_DISPOSITION = "Content-Disposition";
private static final String HEADER_CONTENT_TYPE = "Content-Type";
protected static final String HEADER_CONTENT_DISPOSITION = "Content-Disposition".toUpperCase();
protected static final String HEADER_CONTENT_TYPE = "Content-Type".toUpperCase();
/** This is the delimiter that will separate the fields */
private String delimiter;
@ -67,17 +67,17 @@ public class MultipartParser implements Iterable<MultipartField>{
this.delimiter = delimiter;
this.contentLength = length;
}
public MultipartParser(InputStream in, HttpHeader header){
this(in,
public MultipartParser(HttpHeader header){
this(header.getInputStream(),
parseDelimiter(header.getHeader("Content-type")),
Long.parseLong( header.getHeader("Content-Length")));
Long.parseLong(header.getHeader("Content-Length")));
}
public MultipartParser(HttpServletRequest req) throws IOException {
/* public MultipartParser(HttpServletRequest req) throws IOException {
this(req.getInputStream(),
parseDelimiter(req.getHeader("Content-type")),
req.getContentLength());
}
*/
private static String parseDelimiter(String contentTypeHeader){
String delimiter = contentTypeHeader.split(" *; *")[1];
delimiter = delimiter.split(" *= *")[1];
@ -102,22 +102,20 @@ public class MultipartParser implements Iterable<MultipartField>{
protected class MultiPartIterator implements Iterator<MultipartField>{
private BufferedBoundaryInputStream boundaryIn;
private BufferedReader buffIn;
private HttpHeaderParser parser;
private boolean firstIteration;
protected MultiPartIterator(){
this.boundaryIn = new BufferedBoundaryInputStream(in);
this.buffIn = new BufferedReader(new InputStreamReader(boundaryIn));
this.parser = new HttpHeaderParser(buffIn);
this.parser.setReadStatusLine(false);
this.boundaryIn.setBoundary("--"+delimiter);
firstIteration = true;
}
/**
* TODO: there is a bug where this returns true after the last MultiPart as it cannot read ahead. So use next() != null instead
*/
@Override
public boolean hasNext() {
try {
@ -137,31 +135,26 @@ public class MultipartParser implements Iterable<MultipartField>{
this.boundaryIn.setBoundary("\n--"+delimiter); // Add new-line to boundary after the first iteration
firstIteration = false;
}
String tmp = buffIn.readLine(); // read the new line after the delimiter
String tmp = IOUtil.readLine(boundaryIn); // read the new line after the delimiter
if (tmp == null || tmp.equals("--"))
return null;
HttpHeader header = parser.read();
String disposition = header.getHeader(HEADER_CONTENT_DISPOSITION);
String contentType = header.getHeader("Content-Type");
if (contentType != null && !contentType.equalsIgnoreCase("application/octet-stream"))
logger.warning("Unsupported ontent-Type: "+contentType);
// Read Headers
HashMap<String,String> headers = new HashMap<>();
while ((tmp=IOUtil.readLine(boundaryIn)) != null && !tmp.isEmpty())
HttpHeaderParser.parseHeaderLine(headers, tmp);
// Parse
String disposition = headers.get(HEADER_CONTENT_DISPOSITION);
if (disposition != null){
HashMap<String,String> map = new HashMap<>();
HttpHeaderParser.parseHeaderValue(map, disposition);
if (map.containsKey("form-data")){
if (map.containsKey("filename")){
MultipartFileField field = new MultipartFileField(
map.get("name"),
map.get("filename"),
contentType,
buffIn);
HttpHeaderParser.parseHeaderValue(headers, disposition);
if (headers.containsKey("form-data")){
if (headers.containsKey("filename")){
MultipartFileField field = new MultipartFileField(headers, boundaryIn);
return field;
}
else{
MultipartStringField field = new MultipartStringField(
map.get("name"),
buffIn);
MultipartStringField field = new MultipartStringField(headers, boundaryIn);
return field;
}
}

View file

@ -2,9 +2,11 @@ package zutil.net.http.multipart;
import zutil.io.IOUtil;
import zutil.io.InputStreamCloser;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
@ -15,9 +17,9 @@ public class MultipartStringField implements MultipartField {
private String name;
private String value;
protected MultipartStringField(String name, BufferedReader in) throws IOException {
this.name = name;
value = in.readLine();
protected MultipartStringField(Map<String,String> headers, InputStream in) throws IOException {
this.name = headers.get("name");
value = IOUtil.readLine(in);
}
@Override

View file

@ -78,7 +78,7 @@ public class SOAPClientInvocationHandler implements InvocationHandler {
request.setURL(url);
request.setData(reqXml);
HttpHeaderParser response = request.send();
String rspXml = IOUtil.getContentAsString( request.getResponseReader());
String rspXml = IOUtil.readContentAsString( request.getResponseReader());
// DEBUG
if( logger.isLoggable(Level.FINEST) ){

View file

@ -39,6 +39,8 @@ import zutil.net.http.HttpPrintStream;
import zutil.net.ws.*;
import zutil.net.ws.WSReturnObject.WSValueName;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.Iterator;
@ -120,6 +122,22 @@ public class SOAPHttpPage implements HttpPage{
Map<String, String> request) {
try {
// Read http body
StringBuilder data = null;
String contentType = headers.getHeader("Content-Type");
if (contentType != null &&
(contentType.contains("application/soap+xml") ||
contentType.contains("text/xml") ||
contentType.contains("text/plain"))) {
int post_data_length = Integer.parseInt(headers.getHeader("Content-Length"));
BufferedReader in = new BufferedReader(new InputStreamReader(headers.getInputStream()));
data = new StringBuilder(post_data_length);
for (int i = 0; i < post_data_length; i++) {
data.append((char) in.read());
}
}
// Response
out.setHeader("Content-Type", "text/xml");
out.flush();
@ -138,7 +156,7 @@ public class SOAPHttpPage implements HttpPage{
obj = ws;
}
Document document = genSOAPResponse( request.get(""), obj);
Document document = genSOAPResponse( (data!=null ? data.toString() : ""), obj);
OutputFormat format = OutputFormat.createPrettyPrint();
XMLWriter writer = new XMLWriter( out, format );

View file

@ -70,7 +70,7 @@ public class PluginManager<T> implements Iterable<PluginData>{
log.fine("Searching for plugins...");
for(FileSearcher.FileSearchItem file : search){
try {
DataNode node = JSONParser.read(IOUtil.getContentAsString(file.getInputStream()));
DataNode node = JSONParser.read(IOUtil.readContentAsString(file.getInputStream()));
log.fine("Found plugin: "+file.getPath());
PluginData plugin = new PluginData(node);

View file

@ -38,6 +38,8 @@ public class MultipartParserTest {
assertEquals("foo", stringField.getName());
assertEquals("bar", stringField.getValue());
//assertFalse(it.hasNext()); //TODO: does not work, how to solve this?
assertEquals(null, it.next());
assertEquals(null, it.next());
}
@Test