001/* 002 * Copyright (c) 2009 The openGion Project. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 013 * either express or implied. See the License for the specific language 014 * governing permissions and limitations under the License. 015 */ 016package org.opengion.hayabusa.servlet.multipart; 017 018import java.io.FilterInputStream; 019import java.io.IOException; 020import javax.servlet.ServletInputStream; 021 022/** 023 * ファイルアップロード時のマルチパート処理のファイル読取ストリームです。 024 * 025 * @og.group その他機能 026 * 027 * @version 4.0 028 * @author Kazuhiko Hasegawa 029 * @since JDK5.0, 030 */ 031public class PartInputStream extends FilterInputStream { 032 private final String boundary; 033 private final byte [] buf = new byte[64*1024]; // 64k 034 private int count; 035 private int pos; 036 private boolean eof; 037 038 /** 039 * 読取ストリーム と区切り文字を指定してクラスを構築する コンストラクター 040 * 041 * @param in ServletInputStreamオブジェクト 042 * @param boundary 境界文字 043 * @throws IOException 上位の入出力エラー 044 */ 045 PartInputStream( final ServletInputStream in, final String boundary) throws IOException { 046 super(in); 047 this.boundary = boundary; 048 } 049 050 /** 051 * データを埋めます。 052 * 053 * @throws IOException 入出力エラーが発生したとき 054 */ 055 private void fill() throws IOException { 056 if(eof) { 057 return; 058 } 059 // as long as we are not just starting up 060 if(count > 0) { 061 // if the caller left the requisite amount spare in the buffer 062 if(count - pos == 2) { 063 // copy it back to the start of the buffer 064 System.arraycopy(buf, pos, buf, 0, count - pos); 065 count -= pos; 066 pos = 0; 067 } else { 068 // should never happen, but just in case 069 throw new IllegalStateException("fill() detected illegal buffer state"); 070 } 071 } 072 073 // try and fill the entire buffer, starting at count, line by line 074 // but never read so close to the end that we might split a boundary 075 int read; 076 int maxRead = buf.length - boundary.length(); 077 while (count < maxRead) { 078 // read a line 079 read = ((ServletInputStream)in).readLine(buf, count, buf.length - count); 080 // check for eof and boundary 081 if(read == -1) { 082 throw new IOException("unexpected end of part"); 083 } else { 084 if(read >= boundary.length()) { 085 eof = true; 086 for(int i=0; i < boundary.length(); i++) { 087 if(boundary.charAt(i) != buf[count + i]) { 088 // Not the boundary! 089 eof = false; 090 break; 091 } 092 } 093 if(eof) { 094 break; 095 } 096 } 097 } 098 // success 099 count += read; 100 } 101 } 102 103 /** 104 * データを読み込みます。 105 * 106 * @return 読み取られたデータ 107 * @throws IOException 入出力エラーが発生したとき 108 */ 109 @Override 110 public int read() throws IOException { 111 if(count - pos <= 2) { 112 fill(); 113 if(count - pos <= 2) { 114 return -1; 115 } 116 } 117 return buf[pos++] & 0xff; 118 } 119 120 /** 121 * データを読み込みます。 122 * 123 * @param bt バイト配列 124 * 125 * @return 読み取られたデータ 126 * @throws IOException 入出力エラーが発生したとき 127 */ 128 @Override 129 public int read( final byte[] bt ) throws IOException { 130 return read(bt, 0, bt.length); 131 } 132 133 /** 134 * データを読み込みます。 135 * 136 * @param bt バイト配列 137 * @param off 開始バイト数 138 * @param len 読み取りバイト数 139 * 140 * @return 読み取られたデータ 141 * @throws IOException 入出力エラーが発生したとき 142 */ 143 @Override 144 public int read( final byte[] bt, final int off, final int len ) throws IOException { 145 int total = 0; 146 if(len == 0) { 147 return 0; 148 } 149 150 int avail = count - pos - 2; 151 if(avail <= 0) { 152 fill(); 153 avail = count - pos - 2; 154 if(avail <= 0) { 155 return -1; 156 } 157 } 158 int copy = Math.min(len, avail); 159 System.arraycopy(buf, pos, bt, off, copy); 160 pos += copy; 161 total += copy; 162 163 while (total < len) { 164 fill(); 165 avail = count - pos - 2; 166 if(avail <= 0) { 167 return total; 168 } 169 copy = Math.min(len - total, avail); 170 System.arraycopy(buf, pos, bt, off + total, copy); 171 pos += copy; 172 total += copy; 173 } 174 return total; 175 } 176 177 /** 178 * 利用可能かどうかを返します。 179 * 180 * @return 利用可能かどうか 181 * @throws IOException 入出力エラーが発生したとき 182 */ 183 @Override 184 public int available() throws IOException { 185 int avail = (count - pos - 2) + in.available(); 186 // Never return a negative value 187 return avail < 0 ? 0 : avail; 188 } 189 190 /** 191 * 接続を閉じます。 192 * 193 * @throws IOException 入出力エラーが発生したとき 194 */ 195 @Override 196 public void close() throws IOException { 197 if(!eof) { 198 int size = read(buf, 0, buf.length); 199 while( size != -1) { 200 size = read(buf, 0, buf.length); 201 } 202 } 203 } 204}