I trying to write a code to stream from shoutcast server
and I use the below code and give me javax.sound.sampled.UnsupportedAudioFileException
how I can solve the exception
public static void streamSampledAudio(URL url)
throws IOException, UnsupportedAudioFileException,
LineUnavailableException
{
AudioInputStream ain = null; // We read audio data from here
SourceDataLine line = null; // And write it here.
try {
InputStream is = url.openStream();
BufferedInputStream bis = new BufferedInputStream( is );
ain=AudioSystem.getAudioInputStream(bis);
AudioFormat format = ain.getFormat( );
DataLine.Info info=new DataLine.Info(SourceDataLine.class,format);
if (!AudioSystem.isLineSupported(info)) {
AudioFormat pcm =
new AudioFormat(format.getSampleRate( ), 16,
format.getChannels( ), true, false);
ain = AudioSystem.getAudioInputStream(pcm, ain);
format = ain.getFormat( );
info = new DataLine.Info(SourceDataLine.class, format);
}
line = (SourceDataLine) AudioSystem.getLine(info);
line.open(format);
int framesize = format.getFrameSize( );
byte[ ] buffer = new byte[4 * 1024 * framesize]; // the buffer
int numbytes = 0; // how many bytes
boolean started = false;
for(;;) { // We'll exit the loop when we reach the end of stream
int bytesread=ain.read(buffer,numbytes,buffer.length-numbytes);
if (bytesread == -1) break;
numbytes += bytesread;
if (!started) {
line.start( );
started = true;
}
int bytestowrite = (numbytes/framesize)*framesize;
line.write(buffer, 0, bytestowrite);
int remaining = numbytes - bytestowrite;
if (remaining > 0)
System.arraycopy(buffer,bytestowrite,buffer,0,remaining);
numbytes = remaining;
}
line.drain( );
}
finally { // Always relinquish the resources we use
if (line != null) line.close( );
if (ain != null) ain.close( );
}
}
and give me an exception
Exception in thread "main" javax.sound.sampled.UnsupportedAudioFileException: could not get audio
input stream from input stream
at javax.sound.sampled.AudioSystem.getAudioInputStream(Unknown Source)
at test.PlaySoundStream.streamSampledAudio(PlaySoundStream.java:40)
at test.PlaySoundStream.main(PlaySoundStream.java:21)
can help me to solve the exception
or tell me about away can stream by it from shoutcast
I try this code to download MP3 from shoutcast and then you can play sound
public class DownloadMP3 {
private boolean halt = false;
public static final String DEFAULT_HOST = "127.0.0.1";
protected String host = "url";
public static final int DEFAULT_PORT = 80;
protected int port = 8568;
public static final int DEFAULT_TOTAL_SIZE = 0;
protected long totalSize = 0L;
public static final int DEFAULT_CHUNK_SIZE = 0;
long chunkSize = 0L;
public static final String DEFAULT_OUTPUT_DIRECTORY = ".";
File outputDirectory = new File("D:\\");
public static void main(String[ ] args) throws Exception {
DownloadMP3 d = new DownloadMP3();
d.run();
}
public void run()
{
Socket localSocket = null;
PrintWriter localPrintWriter = null;
BufferedInputStream localBufferedInputStream = null;
FileOutputStream localFileOutputStream = null;
try
{
writeMessage("Opening connection to " + this.host + ":" + this.port);
localSocket = new Socket(this.host, this.port);
localPrintWriter = new PrintWriter(localSocket.getOutputStream(), true);
localBufferedInputStream = new BufferedInputStream(localSocket.getInputStream());
localPrintWriter.print("GET / HTTP/1.0\r\n\r\n");
localPrintWriter.flush();
byte[] arrayOfByte = new byte[1024];
long l1 = 0L;
long l2 = 0L;
int i = 1;
File localFile = null;
writeMessage("Host contacted, waiting for response...");
try
{
int k = 0;
int m;
writeMessage("Recieving Data....");
int j;
while ((j = localBufferedInputStream.read(arrayOfByte)) != -1) {
if ((localFileOutputStream == null) || ((this.chunkSize > 0L) && (l2 + j >= this.chunkSize))) {
m = findSync(arrayOfByte, 0, j);
if (m == -1) {
m = j;
}
if (localFileOutputStream != null)
{
localFileOutputStream.write(arrayOfByte, 0, m);
}
if (localFileOutputStream != null) {
localFileOutputStream.close();
}
while ((localFile = new File(this.outputDirectory, this.host + '-' + this.port + '-' + formatFileNum(i++) + ".mp3")).exists());
writeMessage("Saving to file: " + localFile);
localFileOutputStream = new FileOutputStream(localFile);
l2 = 0L;
localFileOutputStream.write(arrayOfByte, m, j - m);
l2 += j - m;
} else {
localFileOutputStream.write(arrayOfByte, 0, j);
l2 += j;
}
if ((this.totalSize > 0L) && (l1 >= this.totalSize)) {
writeMessage("Capture completed successfully.");
if (this.halt) {
writeMessage("Capture interruted.");
}
}
writeErrorMessage("Connection closed by host.");
return;
} catch (IOException localIOException2) {
if (this.halt)
writeMessage("Capture interruted.");
else {
writeErrorMessage(localIOException2.getMessage());
}
} finally {
if (localFileOutputStream != null)
localFileOutputStream.close();
}
}
catch (UnknownHostException localUnknownHostException) {
writeErrorMessage("Unknown host: " + this.host);
return;
} catch (IOException localIOException1) {
writeErrorMessage("Could not connect to " + this.host + " on port " + this.port);
}
finally {
if (localPrintWriter != null) {
localPrintWriter.close();
}
if (localBufferedInputStream != null)
try {
localBufferedInputStream.close();
}
catch (IOException localIOException3) {
}
if (localSocket != null)
try {
localSocket.close();
}
catch (IOException localIOException4)
{
}
}
}
private static int findSync(byte[] paramArrayOfByte, int paramInt1, int paramInt2)
{
for (int i = paramInt1; i < paramInt2 - 1; i++) {
if (((paramArrayOfByte[i] & 0xFF) == 255) && ((paramArrayOfByte[(i + 1)] & 0xE0) == 224)) {
return i;
}
}
return -1;
}
private static String formatFileNum(int paramInt)
{
if (paramInt < 10)
return "00" + paramInt;
if (paramInt < 100) {
return "0" + paramInt;
}
return "" + paramInt;
}
protected void writeMessage(String paramString)
{
System.out.println(paramString);
}
protected void writeErrorMessage(String paramString)
{
System.err.println(paramString);
}
}
Related
I have tried to get the size of the image by .length in java.
However the original size of the image is several bytes higher than that.
What is the reason for this? Is there any code to get the original size?
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import javax.imageio.ImageIO;
public class Array {
public static void main(String argv[]) throws IOException {
String imageFile1 = "C:/Users/Desktop/4.jpg";
File file = new File(imageFile1);
BufferedImage originalImage = ImageIO.read(file);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(originalImage, "jpg", baos);
byte[] imageInByte = baos.toByteArray();
System.out.println("The length in bytes " + imageInByte.length);
}
}
I think it's about jpeg file header size.
If you want to get an original size when copying image file.
You can use just file copy rather then image file copy.
Or, you can make your own jpeg library, if you really want.
For just one example,
this is one of the old-style code by using java's NIO.
private static void fileCopy(String from, String to) {
FileChannel fromCh = null;
FileChannel toCh = null;
FileInputStream fin = null;
FileOutputStream fout = null;
try {
fin = new FileInputStream(new File(from));
fromCh = fin.getChannel();
fout = new FileOutputStream(new File(to));
toCh = fout.getChannel();
fromCh.transferTo(0, fin.available(), toCh);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fin != null)
try {
fin.close();
} catch (IOException e) {
}
if (fout != null)
try {
fout.close();
} catch (IOException e) {
}
}
}
You can get a file from the original file with the same size.
Check the reference site below:
https://en.wikipedia.org/wiki/JPEG_File_Interchange_Format
I tested it the difference between two file, one is the original another is the copy one. I got an jpeg image from googling.
I modified some code form here in order to analyze the jpeg header file .
Here is the method:
final public static ImageProperties getJpegProperties(File file) throws FileNotFoundException, IOException {
BufferedInputStream in = null;
try {
in = new BufferedInputStream(new FileInputStream(file));
// check for "magic" header
byte[] buf = new byte[2];
int count = in.read(buf, 0, 2);
if (count < 2) {
throw new RuntimeException("Not a valid Jpeg file!");
}
if ((buf[0]) != (byte) 0xFF || (buf[1]) != (byte) 0xD8) {
throw new RuntimeException("Not a valid Jpeg file!");
}
int width = 0;
int height = 0;
char[] comment = null;
boolean hasDims = false;
boolean hasComment = false;
int ch = 0;
int totalHeaderLen = 0;
while (ch != 0xDA && !(hasDims && hasComment)) {
/* Find next marker (JPEG markers begin with 0xFF) */
while (ch != 0xFF) {
ch = in.read();
}
/* JPEG markers can be padded with unlimited 0xFF's */
while (ch == 0xFF) {
ch = in.read();
}
/* Now, ch contains the value of the marker. */
int length = 256 * in.read();
length += in.read();
totalHeaderLen += length;
if (length < 2) {
throw new RuntimeException("Not a valid Jpeg file!");
}
/* Now, length contains the length of the marker. */
if (ch >= 0xC0 && ch <= 0xC3) {
in.read();
height = 256 * in.read();
height += in.read();
width = 256 * in.read();
width += in.read();
for (int foo = 0; foo < length - 2 - 5; foo++) {
in.read();
}
hasDims = true;
} else if (ch == 0xFE) {
// that's the comment marker
comment = new char[length - 2];
for (int foo = 0; foo < length - 2; foo++)
comment[foo] = (char) in.read();
hasComment = true;
} else {
// just skip marker
for (int foo = 0; foo < length - 2; foo++) {
in.read();
}
}
}
if(comment == null) comment = "no comment".toCharArray();
return (new ImageProperties(width, height, new String(comment), totalHeaderLen, "jpeg"));
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
}
}
}
}
The main method is :
public static void main(String argv[]) throws IOException {
String imageFile1 = "resource/4.jpg";
String imageFile2 = "resource/4_jpg.jpg";
//copyImage(imageFile1);
ImageProperties origin = getJpegProperties(new File(imageFile1));
ImageProperties copyed = getJpegProperties(new File(imageFile2));
System.out.println("============ Original one ===========");
System.out.println("comments(origin) : " + origin.getComments());
System.out.println("Height(origin) : " + origin.getHeight());
System.out.println("Width(origin) : " + origin.getWidth());
System.out.println("Header Length(origin) : " + origin.getHeaderLen());
//System.out.println("suffix(origin) : " + origin.getSuffix());
System.out.println();
System.out.println("============ Copy one ===========");
System.out.println("comments(copy) : " + copyed.getComments());
System.out.println("Height(copy) : " + copyed.getHeight());
System.out.println("Width(copy) : " + copyed.getWidth());
System.out.println("Header Length(copy) : " + copyed.getHeaderLen());
//System.out.println("suffix(copy) : " + copyed.getSuffix());
}
I copy the original image first using copyImage method here.
static BufferedImage copyImage(BufferedImage source) {
BufferedImage b = new BufferedImage(source.getWidth(), source.getHeight(), source.getType());
Graphics g = b.getGraphics();
g.drawImage(source, 0, 0, null);
g.dispose();
return b;
}
I can see the difference of the two images in the explorer.
I got a different header size when running the program.
The output:
============ Original one ===========
comments(origin) : no comment
Height(origin) : 534
Width(origin) : 800
Header Length(origin) : 21269
============ Copy one ===========
comments(copy) : no comment
Height(copy) : 534
Width(copy) : 800
Header Length(copy) : 603
Header Length is different as you can see the result.
Here is full test code.
package stackoverflow;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
public class Misc {
public static void main(String argv[]) throws IOException {
String imageFile1 = "resource/4.jpg";
String imageFile2 = "resource/4_jpg.jpg";
String imageFile3 = "resource/4_org.jpg";
fileCopy(imageFile1, imageFile3);
//copyImage(imageFile1);
ImageProperties origin = getJpegProperties(new File(imageFile1));
ImageProperties copyed = getJpegProperties(new File(imageFile2));
System.out.println("============ Original one ===========");
System.out.println("comments(origin) : " + origin.getComments());
System.out.println("Height(origin) : " + origin.getHeight());
System.out.println("Width(origin) : " + origin.getWidth());
System.out.println("Header Length(origin) : " + origin.getHeaderLen());
//System.out.println("suffix(origin) : " + origin.getSuffix());
System.out.println();
System.out.println("============ Copy one ===========");
System.out.println("comments(copy) : " + copyed.getComments());
System.out.println("Height(copy) : " + copyed.getHeight());
System.out.println("Width(copy) : " + copyed.getWidth());
System.out.println("Header Length(copy) : " + copyed.getHeaderLen());
//System.out.println("suffix(copy) : " + copyed.getSuffix());
}
static class ImageProperties {
private final int width;
private final int height;
private final String comments;
private final int headerLen;
private final String suffix;
public ImageProperties(
final int width, final int height, final String comments, final int headerLen,
final String suffix)
{
this.width = width;
this.height = height;
this.comments = comments;
this.suffix = suffix;
this.headerLen = headerLen;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public String getComments() {
return comments;
}
public String getSuffix() {
return suffix;
}
public int getHeaderLen() {
return headerLen;
}
}
final public static ImageProperties getJpegProperties(File file) throws FileNotFoundException, IOException {
BufferedInputStream in = null;
try {
in = new BufferedInputStream(new FileInputStream(file));
// check for "magic" header
byte[] buf = new byte[2];
int count = in.read(buf, 0, 2);
if (count < 2) {
throw new RuntimeException("Not a valid Jpeg file!");
}
if ((buf[0]) != (byte) 0xFF || (buf[1]) != (byte) 0xD8) {
throw new RuntimeException("Not a valid Jpeg file!");
}
int width = 0;
int height = 0;
char[] comment = null;
boolean hasDims = false;
boolean hasComment = false;
int ch = 0;
int totalHeaderLen = 0;
while (ch != 0xDA && !(hasDims && hasComment)) {
/* Find next marker (JPEG markers begin with 0xFF) */
while (ch != 0xFF) {
ch = in.read();
}
/* JPEG markers can be padded with unlimited 0xFF's */
while (ch == 0xFF) {
ch = in.read();
}
/* Now, ch contains the value of the marker. */
int length = 256 * in.read();
length += in.read();
totalHeaderLen += length;
if (length < 2) {
throw new RuntimeException("Not a valid Jpeg file!");
}
/* Now, length contains the length of the marker. */
if (ch >= 0xC0 && ch <= 0xC3) {
in.read();
height = 256 * in.read();
height += in.read();
width = 256 * in.read();
width += in.read();
for (int foo = 0; foo < length - 2 - 5; foo++) {
in.read();
}
hasDims = true;
} else if (ch == 0xFE) {
// that's the comment marker
comment = new char[length - 2];
for (int foo = 0; foo < length - 2; foo++)
comment[foo] = (char) in.read();
hasComment = true;
} else {
// just skip marker
for (int foo = 0; foo < length - 2; foo++) {
in.read();
}
}
}
if(comment == null) comment = "no comment".toCharArray();
return (new ImageProperties(width, height, new String(comment), totalHeaderLen, "jpeg"));
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
}
}
}
}
static BufferedImage copyImage(BufferedImage source) {
BufferedImage b = new BufferedImage(source.getWidth(), source.getHeight(), source.getType());
Graphics g = b.getGraphics();
g.drawImage(source, 0, 0, null);
g.dispose();
return b;
}
private static void fileCopy(String from, String to) {
FileChannel fromCh = null;
FileChannel toCh = null;
FileInputStream fin = null;
FileOutputStream fout = null;
try {
fin = new FileInputStream(new File(from));
fromCh = fin.getChannel();
fout = new FileOutputStream(new File(to));
toCh = fout.getChannel();
fromCh.transferTo(0, fin.available(), toCh);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fin != null)
try {
fin.close();
} catch (IOException e) {
}
if (fout != null)
try {
fout.close();
} catch (IOException e) {
}
}
}
}
So, I think the extra information of the jpeg image skipped by the image io library in java.
Alright so I am looking at this piece of code that is supposed to get an array of bytes which represents an image and send it piece by piece to a server. The server needs to be told when the image transmission is done, this ending message is "CLOSE". Everything works fine but unless I uncomment Thread.sleep the end message isn't sent. Also the delay needs to be quite big for some reason, 100 ms for example doesn't work. If anyone could provide an explanation for this behaviour I would be grateful since I don't have a very good understanding of java.
private class NetTask extends AsyncTask<Void, Void, Void>
{
private String ip;
private byte[] to_send;
public NetTask(String ip, byte[] to_send)
{
this.ip = ip;
this.to_send = to_send;
}
#Override
protected Void doInBackground(Void...params)
{
try {
Log.i(dTag, "" + to_send.length);
Socket sck = new Socket(ip, 1234);
DataOutputStream dOut = new DataOutputStream(sck.getOutputStream());
dOut.write(ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(to_send.length).array());
Log.d(dTag, "" + to_send.length);
int x = 500;
int len = to_send.length;
for (int i = 0; i < len - x + 1; i += x)
dOut.write(Arrays.copyOfRange(to_send, i, i + x));
if (len % x != 0)
dOut.write(Arrays.copyOfRange(to_send, len - len % x, len));
/*try {
Thread.sleep(1000);
}
catch (Exception ex) {
Log.d(dTag, "thread sleep error");
}*/
dOut.write("CLOSE".getBytes());
dOut.flush();
dOut.close();
sck.close();
}
catch (IOException ex) {
Log.d(dTag, ex.getMessage());
}
return null;
}
}
The server is in c#, here is the code:
while (ok)
{
sck.Listen(1000);
Socket accepted = sck.Accept();
buffer = new byte[accepted.SendBufferSize];
int bytesRead = -1;
bool reading = true;
int im_size = -1;
int index = 0;
byte[] image = null;
while (reading)
{
bytesRead = accepted.Receive(buffer);
if (bytesRead == 5)
Console.WriteLine(bytesRead);
string strData = "YADA";
byte[] formatted = new byte[bytesRead];
if (bytesRead == 5)
{
for (int i = 0; i < bytesRead; i++)
{
formatted[i] = buffer[i];
}
strData = Encoding.ASCII.GetString(formatted);
}
if (strData == "CLOSE")
{
Console.WriteLine("GOT CLOSE MESSAGE");
Image im = Image.FromStream(new MemoryStream(image));
im.Save(#"D:\im1.bmp");
}
else
{
if (im_size == -1)
{
im_size = BitConverter.ToInt32(buffer, 0);
image = new byte[im_size];
Console.WriteLine(im_size);
}
else
{
for (int i = 0; i < bytesRead && index < im_size; i++)
{
image[index++] = buffer[i];
}
}
}
}
accepted.Close();
}
I am working on task to implement external authentication in ejabberd using java.
I searched for the examples on internet and found examples in PHP, Perl, Python but could not find any example in java.
I know the configuration that is required to be made in 'ejabberd.cfg' file.
Any code sample in java will be very helpful.
Try this:
public static void main(String[] args) {
try {
outerloop: while (true) {
byte[] lB = new byte[2];
int startPos = 0;
while (startPos < lB.length) {
int ret = System.in.read(lB, startPos,
(lB.length - startPos));
if (ret < 0) {
break outerloop;
}
startPos += ret;
}
int streamLen = System.in.available();
byte[] rd = new byte[streamLen];
startPos = 0;
while (startPos < streamLen) {
int ret = System.in.read(rd, startPos,
(streamLen - startPos));
if (ret < 0) {
break outerloop;
}
startPos += ret;
}
String inputArgs = new String(rd, "ASCII");
String[] arguments = inputArgs.split(":");
String userName = arguments[1];
String password = arguments[3];
//
// Here do the authentication
//
boolean resultOfAuthentication = // Result of Authentication;
byte[] res = new byte[4];
res[0] = 0;
res[1] = 2;
res[2] = 0;
if (resultOfAuthentication) {
res[3] = 1;
} else {
res[3] = 0;
}
System.out.write(res, 0, res.length);
System.out.flush();
}
} catch (Exception e) {
System.out.println("ERROR");
}
}
I have an mp3 file, and an image. I need to create a video combining them, in java.
I'm trying to do it with xuggle, but there are still no results.
Can anybody give me any suggestions ?
Finally, I found a solution.
I used pieces of code from Xuggle's examples.
I also solved a problem with audio transcoding.
I'll write my code here, because I cannot explain why it works, but it just works.
public String make() throws IOException, InterruptedException {
BufferedImage s1 = genImage();
writer = ToolFactory.makeWriter("temp/" + sermon.getFile().getName() + ".flv");
String filename = sermon.getFile().getAbsolutePath();
IContainer container = IContainer.make();
if (container.open(filename, IContainer.Type.READ, null) < 0) {
throw new IllegalArgumentException("could not open file: " + filename);
}
int numStreams = container.getNumStreams();
int audioStreamId = -1;
IStreamCoder audioCoder = null;
for (int i = 0; i < numStreams; i++) {
IStream stream = container.getStream(i);
IStreamCoder coder = stream.getStreamCoder();
if (coder.getCodecType() == ICodec.Type.CODEC_TYPE_AUDIO) {
audioStreamId = i;
audioCoder = coder;
break;
}
}
if (audioStreamId == -1) {
throw new RuntimeException("could not find audio stream in container: " + filename);
}
if (audioCoder.open() < 0) {
throw new RuntimeException("could not open audio decoder for container: " + filename);
}
writer.addAudioStream(0, 0, audioCoder.getChannels(), audioCoder.getSampleRate());
writer.addVideoStream(1, 1, width, height);
IPacket packet = IPacket.make();
int n = 0;
while (container.readNextPacket(packet) >= 0) {
n++;
if (packet.getStreamIndex() == audioStreamId) {
IAudioSamples samples = IAudioSamples.make(2048, audioCoder.getChannels());
int offset = 0;
while (offset < packet.getSize()) {
try {
int bytesDecoded = audioCoder.decodeAudio(samples, packet, offset);
if (bytesDecoded < 0) {
//throw new RuntimeException("got error decoding audio in: " + filename);
break;
}
offset += bytesDecoded;
if (samples.isComplete()) {
if (n % 1000 == 0) {
writer.flush();
System.out.println(n);
System.gc();
}
writer.encodeAudio(0, samples);
}
} catch (Exception e) {
System.out.println(e);
}
}
} else {
do {
} while (false);
}
}
for (int i = 0; i < container.getDuration() / 1000000; i++) {
writer.encodeVideo(1, s1, i, TimeUnit.SECONDS);
}
writer.close();
if (audioCoder != null) {
audioCoder.close();
audioCoder = null;
}
if (container != null) {
container.close();
container = null;
}
return "temp/" + sermon.getFile().getName() + ".flv";
}
Thanks, good luck.
I want to read file in opposite direction from end to the start my file,
[1322110800] LOG ROTATION: DAILY
[1322110800] LOG VERSION: 2.0
[1322110800] CURRENT HOST STATE:arsalan.hussain;DOWN;HARD;1;CRITICAL - Host Unreachable (192.168.1.107)
[1322110800] CURRENT HOST STATE: localhost;UP;HARD;1;PING OK - Packet loss = 0%, RTA = 0.06 ms
[1322110800] CURRENT HOST STATE: musewerx-72c7b0;UP;HARD;1;PING OK - Packet loss = 0%, RTA = 0.27 ms
i use code to read it in this way,
String strpath="/var/nagios.log";
FileReader fr = new FileReader(strpath);
BufferedReader br = new BufferedReader(fr);
String ch;
int time=0;
String Conversion="";
do {
ch = br.readLine();
out.print(ch+"<br/>");
} while (ch != null);
fr.close();
I would prefer to read in reverse order using buffer reader
I had the same problem as described here. I want to look at lines in file in reverse order, from the end back to the start (The unix tac command will do it).
However my input files are fairly large so reading the whole file into memory, as in the other examples was not really a workable option for me.
Below is the class I came up with, it does use RandomAccessFile, but does not need any buffers, since it just retains pointers to the file itself, and works with the standard InputStream methods.
It works for my cases, and empty files and a few other things I've tried. Now I don't have Unicode characters or anything fancy, but as long as the lines are delimited by LF, and even if they have a LF + CR it should work.
Basic Usage is :
in = new BufferedReader (new InputStreamReader (new ReverseLineInputStream(file)));
while(true) {
String line = in.readLine();
if (line == null) {
break;
}
System.out.println("X:" + line);
}
Here is the main source:
package www.kosoft.util;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
public class ReverseLineInputStream extends InputStream {
RandomAccessFile in;
long currentLineStart = -1;
long currentLineEnd = -1;
long currentPos = -1;
long lastPosInFile = -1;
public ReverseLineInputStream(File file) throws FileNotFoundException {
in = new RandomAccessFile(file, "r");
currentLineStart = file.length();
currentLineEnd = file.length();
lastPosInFile = file.length() -1;
currentPos = currentLineEnd;
}
public void findPrevLine() throws IOException {
currentLineEnd = currentLineStart;
// There are no more lines, since we are at the beginning of the file and no lines.
if (currentLineEnd == 0) {
currentLineEnd = -1;
currentLineStart = -1;
currentPos = -1;
return;
}
long filePointer = currentLineStart -1;
while ( true) {
filePointer--;
// we are at start of file so this is the first line in the file.
if (filePointer < 0) {
break;
}
in.seek(filePointer);
int readByte = in.readByte();
// We ignore last LF in file. search back to find the previous LF.
if (readByte == 0xA && filePointer != lastPosInFile ) {
break;
}
}
// we want to start at pointer +1 so we are after the LF we found or at 0 the start of the file.
currentLineStart = filePointer + 1;
currentPos = currentLineStart;
}
public int read() throws IOException {
if (currentPos < currentLineEnd ) {
in.seek(currentPos++);
int readByte = in.readByte();
return readByte;
}
else if (currentPos < 0) {
return -1;
}
else {
findPrevLine();
return read();
}
}
}
Apache Commons IO has the ReversedLinesFileReader class for this now (well, since version 2.2).
So your code could be:
String strpath="/var/nagios.log";
ReversedLinesFileReader fr = new ReversedLinesFileReader(new File(strpath));
String ch;
int time=0;
String Conversion="";
do {
ch = fr.readLine();
out.print(ch+"<br/>");
} while (ch != null);
fr.close();
The ReverseLineInputStream posted above is exactly what I was looking for. The files I am reading are large and cannot be buffered.
There are a couple of bugs:
File is not closed
if the last line is not terminated the last 2 lines are returned on the first read.
Here is the corrected code:
package www.kosoft.util;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
public class ReverseLineInputStream extends InputStream {
RandomAccessFile in;
long currentLineStart = -1;
long currentLineEnd = -1;
long currentPos = -1;
long lastPosInFile = -1;
int lastChar = -1;
public ReverseLineInputStream(File file) throws FileNotFoundException {
in = new RandomAccessFile(file, "r");
currentLineStart = file.length();
currentLineEnd = file.length();
lastPosInFile = file.length() -1;
currentPos = currentLineEnd;
}
private void findPrevLine() throws IOException {
if (lastChar == -1) {
in.seek(lastPosInFile);
lastChar = in.readByte();
}
currentLineEnd = currentLineStart;
// There are no more lines, since we are at the beginning of the file and no lines.
if (currentLineEnd == 0) {
currentLineEnd = -1;
currentLineStart = -1;
currentPos = -1;
return;
}
long filePointer = currentLineStart -1;
while ( true) {
filePointer--;
// we are at start of file so this is the first line in the file.
if (filePointer < 0) {
break;
}
in.seek(filePointer);
int readByte = in.readByte();
// We ignore last LF in file. search back to find the previous LF.
if (readByte == 0xA && filePointer != lastPosInFile ) {
break;
}
}
// we want to start at pointer +1 so we are after the LF we found or at 0 the start of the file.
currentLineStart = filePointer + 1;
currentPos = currentLineStart;
}
public int read() throws IOException {
if (currentPos < currentLineEnd ) {
in.seek(currentPos++);
int readByte = in.readByte();
return readByte;
} else if (currentPos > lastPosInFile && currentLineStart < currentLineEnd) {
// last line in file (first returned)
findPrevLine();
if (lastChar != '\n' && lastChar != '\r') {
// last line is not terminated
return '\n';
} else {
return read();
}
} else if (currentPos < 0) {
return -1;
} else {
findPrevLine();
return read();
}
}
#Override
public void close() throws IOException {
if (in != null) {
in.close();
in = null;
}
}
}
The proposed ReverseLineInputStream works really slow when you try to read thousands of lines. At my PC Intel Core i7 on SSD drive it was about 60k lines in 80 seconds. Here is the inspired optimized version with buffered reading (opposed to one-byte-at-a-time reading in ReverseLineInputStream). 60k lines log file is read in 400 milliseconds:
public class FastReverseLineInputStream extends InputStream {
private static final int MAX_LINE_BYTES = 1024 * 1024;
private static final int DEFAULT_BUFFER_SIZE = 1024 * 1024;
private RandomAccessFile in;
private long currentFilePos;
private int bufferSize;
private byte[] buffer;
private int currentBufferPos;
private int maxLineBytes;
private byte[] currentLine;
private int currentLineWritePos = 0;
private int currentLineReadPos = 0;
private boolean lineBuffered = false;
public ReverseLineInputStream(File file) throws IOException {
this(file, DEFAULT_BUFFER_SIZE, MAX_LINE_BYTES);
}
public ReverseLineInputStream(File file, int bufferSize, int maxLineBytes) throws IOException {
this.maxLineBytes = maxLineBytes;
in = new RandomAccessFile(file, "r");
currentFilePos = file.length() - 1;
in.seek(currentFilePos);
if (in.readByte() == 0xA) {
currentFilePos--;
}
currentLine = new byte[maxLineBytes];
currentLine[0] = 0xA;
this.bufferSize = bufferSize;
buffer = new byte[bufferSize];
fillBuffer();
fillLineBuffer();
}
#Override
public int read() throws IOException {
if (currentFilePos <= 0 && currentBufferPos < 0 && currentLineReadPos < 0) {
return -1;
}
if (!lineBuffered) {
fillLineBuffer();
}
if (lineBuffered) {
if (currentLineReadPos == 0) {
lineBuffered = false;
}
return currentLine[currentLineReadPos--];
}
return 0;
}
private void fillBuffer() throws IOException {
if (currentFilePos < 0) {
return;
}
if (currentFilePos < bufferSize) {
in.seek(0);
in.read(buffer);
currentBufferPos = (int) currentFilePos;
currentFilePos = -1;
} else {
in.seek(currentFilePos);
in.read(buffer);
currentBufferPos = bufferSize - 1;
currentFilePos = currentFilePos - bufferSize;
}
}
private void fillLineBuffer() throws IOException {
currentLineWritePos = 1;
while (true) {
// we've read all the buffer - need to fill it again
if (currentBufferPos < 0) {
fillBuffer();
// nothing was buffered - we reached the beginning of a file
if (currentBufferPos < 0) {
currentLineReadPos = currentLineWritePos - 1;
lineBuffered = true;
return;
}
}
byte b = buffer[currentBufferPos--];
// \n is found - line fully buffered
if (b == 0xA) {
currentLineReadPos = currentLineWritePos - 1;
lineBuffered = true;
break;
// just ignore \r for now
} else if (b == 0xD) {
continue;
} else {
if (currentLineWritePos == maxLineBytes) {
throw new IOException("file has a line exceeding " + maxLineBytes
+ " bytes; use constructor to pickup bigger line buffer");
}
// write the current line bytes in reverse order - reading from
// the end will produce the correct line
currentLine[currentLineWritePos++] = b;
}
}
}}
#Test
public void readAndPrintInReverseOrder() throws IOException {
String path = "src/misctests/test.txt";
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(path));
Stack<String> lines = new Stack<String>();
String line = br.readLine();
while(line != null) {
lines.push(line);
line = br.readLine();
}
while(! lines.empty()) {
System.out.println(lines.pop());
}
} finally {
if(br != null) {
try {
br.close();
} catch(IOException e) {
// can't help it
}
}
}
}
Note that this code reads the hole file into memory and then starts printing it. This is the only way you can do it with a buffered reader or anry other reader that does not support seeking. You have to keep this in mind, in your case you want to read a log file, log files can be very big!
If you want to read line by line and print on the fly then you have no other alternative than using a reader that support seeking such as java.io.RandomAccessFile and this anything but trivial.
As far as I understand, you try to read backwards line by line.
Suppose this is the file you try to read:
line1
line2
line3
And you want to write it to the output stream of the servlet as follows:
line3
line2
line1
Following code might be helpful in this case:
List<String> tmp = new ArrayList<String>();
do {
ch = br.readLine();
tmp.add(ch);
out.print(ch+"<br/>");
} while (ch != null);
for(int i=tmp.size()-1;i>=0;i--) {
out.print(tmp.get(i)+"<br/>");
}
I had a problem with your solution #dpetruha because of this:
Does RandomAccessFile.read() from local file guarantee that exact number of bytes will be read?
Here is my solution: (changed only fillBuffer)
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
public class ReverseLineInputStream extends InputStream {
private static final int MAX_LINE_BYTES = 1024 * 1024;
private static final int DEFAULT_BUFFER_SIZE = 1024 * 1024;
private RandomAccessFile in;
private long currentFilePos;
private int bufferSize;
private byte[] buffer;
private int currentBufferPos;
private int maxLineBytes;
private byte[] currentLine;
private int currentLineWritePos = 0;
private int currentLineReadPos = 0;
private boolean lineBuffered = false;
public ReverseLineInputStream(File file) throws IOException {
this(file, DEFAULT_BUFFER_SIZE, MAX_LINE_BYTES);
}
public ReverseLineInputStream(File file, int bufferSize, int maxLineBytes) throws IOException {
this.maxLineBytes = maxLineBytes;
in = new RandomAccessFile(file, "r");
currentFilePos = file.length() - 1;
in.seek(currentFilePos);
if (in.readByte() == 0xA) {
currentFilePos--;
}
currentLine = new byte[maxLineBytes];
currentLine[0] = 0xA;
this.bufferSize = bufferSize;
buffer = new byte[bufferSize];
fillBuffer();
fillLineBuffer();
}
#Override
public int read() throws IOException {
if (currentFilePos <= 0 && currentBufferPos < 0 && currentLineReadPos < 0) {
return -1;
}
if (!lineBuffered) {
fillLineBuffer();
}
if (lineBuffered) {
if (currentLineReadPos == 0) {
lineBuffered = false;
}
return currentLine[currentLineReadPos--];
}
return 0;
}
private void fillBuffer() throws IOException {
if (currentFilePos < 0) {
return;
}
if (currentFilePos < bufferSize) {
in.seek(0);
buffer = new byte[(int) currentFilePos + 1];
in.readFully(buffer);
currentBufferPos = (int) currentFilePos;
currentFilePos = -1;
} else {
in.seek(currentFilePos - buffer.length);
in.readFully(buffer);
currentBufferPos = bufferSize - 1;
currentFilePos = currentFilePos - bufferSize;
}
}
private void fillLineBuffer() throws IOException {
currentLineWritePos = 1;
while (true) {
// we've read all the buffer - need to fill it again
if (currentBufferPos < 0) {
fillBuffer();
// nothing was buffered - we reached the beginning of a file
if (currentBufferPos < 0) {
currentLineReadPos = currentLineWritePos - 1;
lineBuffered = true;
return;
}
}
byte b = buffer[currentBufferPos--];
// \n is found - line fully buffered
if (b == 0xA) {
currentLineReadPos = currentLineWritePos - 1;
lineBuffered = true;
break;
// just ignore \r for now
} else if (b == 0xD) {
continue;
} else {
if (currentLineWritePos == maxLineBytes) {
throw new IOException("file has a line exceeding " + maxLineBytes
+ " bytes; use constructor to pickup bigger line buffer");
}
// write the current line bytes in reverse order - reading from
// the end will produce the correct line
currentLine[currentLineWritePos++] = b;
}
}
}
}