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 jakarta.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        /* default */ 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                final 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                if(len == 0) {
146                        return 0;
147                }
148
149                int avail = count - pos - 2;
150                if(avail <= 0) {
151                        fill();
152                        avail = count - pos - 2;
153                        if(avail <= 0) {
154                                return -1;
155                        }
156                }
157                int copy = Math.min(len, avail);
158                System.arraycopy(buf, pos, bt, off, copy);
159                pos   += copy;
160                int total = copy;                       // 6.1.0.0 (2014/12/26) refactoring
161
162                while( total < len ) {
163                        fill();
164                        avail = count - pos - 2;
165                        if(avail <= 0) {
166                                return total;
167                        }
168                        copy = Math.min(len - total, avail);
169                        System.arraycopy(buf, pos, bt, off + total, copy);
170                        pos   += copy;
171                        total += copy;
172                }
173                return total;
174        }
175
176        /**
177         * 利用可能かどうかを返します。
178         *
179         * @return      利用可能かどうか
180         * @throws IOException 入出力エラーが発生したとき
181         */
182        @Override
183        public int available() throws IOException {
184                final int avail = (count - pos - 2) + in.available();
185                // Never return a negative value
186                return avail < 0 ? 0 : avail;
187        }
188
189        /**
190         * 接続を閉じます。
191         *
192         * @throws IOException 入出力エラーが発生したとき
193         */
194        @Override
195        public void close() throws IOException {
196                if(!eof) {
197                        int size = read(buf, 0, buf.length);
198                        while( size != -1) {
199                                size = read(buf, 0, buf.length);
200                        }
201                }
202        }
203}