    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        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}