原因是公司最近有个需求,上传一个utf8编码的csv到ftp,供下游系统使用。但是下游系统要求上传到ftp的文件是GBK编码的。方案一是大体思路,直接使用InputStreamReader和OutputStreamWriter读写文件即可(复制别人的代码)importjava.io.*;//Convertfileencoding:将GBK编码的文件转为UTF-8编码的文件publicclassGBKtoUtf{publicstaticvoidmain(String[]args)throwsIOException{InputStreamReaderinputStreamReader=newInputStreamReader(newFileInputStream("C:\\test\\GBKfile.txt"),"GBK");//这里必须是GBKOutputStreamWriteroutputStreamWriter=newOutputStreamWriter(newFileOutputStream("C:\\test\\UTF-8file.txt"),"UTF-8");intlen=0;while((len=inputStreamReader.read())!=-1){outputStreamWriter.write(len);}outputStreamWriter.close();inputStreamReader.close();}}方案二对于小文件,方案一比较简单明了。对于大文件,方案1需要读写磁盘,性能很低(磁盘往往是操作系统的瓶颈)。上传文件,必须有另一个文件读取。有没有办法在读取过程中完成转码?分析FtpClient的API可知,FtpClient上传的文件其实只是字节流的一份拷贝。OutputStreamWriter分析查看OutputStreamWriter的源码,发现它使用了一个StreamEncoder类来实现编码转换,而StreamEncoder使用CharsetEncoder实现编码转换。CharsetEncoder有两个转换APIpublicfinalCoderResultencode(CharBufferin,ByteBufferout,booleanendOfInput){......}publicfinalByteBufferencode(CharBufferin){......}文件读取过程完成转码.由于FtpClient上传的文件只是字节流的一份副本,所以只需要在读取字节流时完成即可。转码没问题。CharsetEncoder提供了将CharBuffer转换为ByteBuffer的API。所以,只需要实现一个自定义的InputStream即可importorg.apache.commons.io.IOUtils;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importjava.io.*;importjava.nio.ByteBuffer;导入java.nio.CharBuffer;导入java.nio.charset.Charset;导入java.nio.charset.CharsetEncoder;导入java.nio.charset.CoderResult;导入java.nio.charset.StandardCharsets;公共类FtpInputStream扩展InputStream{privatestaticfinalLoggerlogger=LoggerFactory.getLogger(FtpInputStream.class);私有最终InputStreamReaderinputStreamReader;私有最终字符集originCharset;私人最终字符集destCharset;私有最终CharsetEncoder编码器;私有最终CharBuffercharBuffer;私有最终ByteBufferbyteBuffer;私人CoderResultcoderResult=CoderResult.UNDERFLOW;私人布尔结束=假;publicFtpInputStream(InputStreaminputStream,CharsetoriginCharset,CharsetdestCharset){this.inputStreamReader=newInputStreamReader(inputStream,originCharset);this.originCharset=originCharset;this.destCharset=destCharset;this.encoder=destCharset.newEncoder();this.charBuffer=CharBuffer.allocate(1000);this.byteBuffer=ByteBuffer.allocate((int)(1000*encoder.averageBytesPerChar()));这个.byteBuffer.flip();}@Overridepublicvoidclose()throwsIOException{inputStreamReader.close();}@Overridepublicintread()throwsIOException{if(byteBuffer.hasRemaining()){//byteBuffer还有数据,直接读取returnbyteBuffer.get();}else{//byteBuffer已被读取,清除它以供使用byteBuffer.clear();}如果(结束){返回-1;}//byteBuffer没有数据,charBuffer有两种情况:已经消费,没有消费//已经消费,需要从inputStreamReader重新读取//没有消费,不需要从inputStreamReader读取inputStreamReader读取,直接编码if(coderResult.isUnderflow()){charBuffer.clear();intr=inputStreamReader.read(charBuffer);if(r==-1){//阅读结束end=true;charBuffer.翻转();}elseif(r==0){//什么不应该抛出newIOException("read0chart");}else{//要被编码器读取的翻转charBuffer.flip();}}//编码成字节coderResult=encoder.encode(charBuffer,byteBuffer,end);如果(coderResult.isError()){抛出新的IOException(coderResult.toString());}if(end){encoder.flush(byteBuffer);}//翻转,准备读取byteBuffer.flip();if(byteBuffer.hasRemaining()){返回byteBuffer.get();}else{//不应该被清除thrownewIOException("encoderesultisnull");}}}
