Related
I fetch inputstream from mongodb and pass it to video.js to play.
It plays completely fine first time, once video is finished and I click on 'play' button to play again then i got below exception in java code
java.net.SocketException: Software caused connection abort: socket write error
My Java code is -
#RequestMapping(value = "/getvideo/{videoId}" , method = RequestMethod.GET)
public void fetchvideo(#PathVariable(value = "videoId") String videoId, HttpServletResponse response, HttpServletRequest request) {
try{
GridFSDBFile file = fileStorageService.getFileById(videoId);
file.getInputStream();
response.setHeader("Content-Type", file.getContentType());
response.setHeader("X-Content-Type-Options", "nosniff");
response.setHeader("Accept-Ranges", "bytes");
response.setContentLength((int) file.getLength());
file.writeTo(response.getOutputStream());
file.getInputStream().close();
} catch(Exception e) {
e.printStackTrace();
}
}
Html code -
<video id="galleryVideoId" class="hide-normal video-js vjs-default-skin vjs-big-play-centered" autoplay="autoplay" controls="controls" preload="auto" width="100%" height="100%" data-setup="{}">
<source th:src="#{'/getvideo/' + ${videoId} }" type='video/mp4'/>
</video>
I tried to search on google and tried few solutions as well but didn't work out.
Please guide. Thanks.
This same issue I faced before and below is the exact solution of it.
Try below code :
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.FileTime;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* See full code here : https://github.com/davinkevin/Podcast-Server/blob/d927d9b8cb9ea1268af74316cd20b7192ca92da7/src/main/java/lan/dk/podcastserver/utils/multipart/MultipartFileSender.java
*/
public class MultipartFileSender {
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
private static final int DEFAULT_BUFFER_SIZE = 20480; // ..bytes = 20KB.
private static final long DEFAULT_EXPIRE_TIME = 604800000L; // ..ms = 1 week.
private static final String MULTIPART_BOUNDARY = "MULTIPART_BYTERANGES";
Path filepath;
HttpServletRequest request;
HttpServletResponse response;
public MultipartFileSender() {
}
public static MultipartFileSender fromPath(Path path) {
return new MultipartFileSender().setFilepath(path);
}
public static MultipartFileSender fromFile(File file) {
return new MultipartFileSender().setFilepath(file.toPath());
}
public static MultipartFileSender fromURIString(String uri) {
return new MultipartFileSender().setFilepath(Paths.get(uri));
}
//** internal setter **//
private MultipartFileSender setFilepath(Path filepath) {
this.filepath = filepath;
return this;
}
public MultipartFileSender with(HttpServletRequest httpRequest) {
request = httpRequest;
return this;
}
public MultipartFileSender with(HttpServletResponse httpResponse) {
response = httpResponse;
return this;
}
public void serveResource() throws Exception {
if (response == null || request == null) {
return;
}
if (!Files.exists(filepath)) {
logger.error("File doesn't exist at URI : {}", filepath.toAbsolutePath().toString());
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
Long length = Files.size(filepath);
String fileName = filepath.getFileName().toString();
FileTime lastModifiedObj = Files.getLastModifiedTime(filepath);
if (StringUtils.isEmpty(fileName) || lastModifiedObj == null) {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return;
}
long lastModified = LocalDateTime.ofInstant(lastModifiedObj.toInstant(), ZoneId.of(ZoneOffset.systemDefault().getId())).toEpochSecond(ZoneOffset.UTC);
String contentType = "video/mp4";
// Validate request headers for caching ---------------------------------------------------
// If-None-Match header should contain "*" or ETag. If so, then return 304.
String ifNoneMatch = request.getHeader("If-None-Match");
if (ifNoneMatch != null && HttpUtils.matches(ifNoneMatch, fileName)) {
response.setHeader("ETag", fileName); // Required in 304.
response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
return;
}
// If-Modified-Since header should be greater than LastModified. If so, then return 304.
// This header is ignored if any If-None-Match header is specified.
long ifModifiedSince = request.getDateHeader("If-Modified-Since");
if (ifNoneMatch == null && ifModifiedSince != -1 && ifModifiedSince + 1000 > lastModified) {
response.setHeader("ETag", fileName); // Required in 304.
response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
return;
}
// Validate request headers for resume ----------------------------------------------------
// If-Match header should contain "*" or ETag. If not, then return 412.
String ifMatch = request.getHeader("If-Match");
if (ifMatch != null && !HttpUtils.matches(ifMatch, fileName)) {
response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
return;
}
// If-Unmodified-Since header should be greater than LastModified. If not, then return 412.
long ifUnmodifiedSince = request.getDateHeader("If-Unmodified-Since");
if (ifUnmodifiedSince != -1 && ifUnmodifiedSince + 1000 <= lastModified) {
response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
return;
}
// Validate and process range -------------------------------------------------------------
// Prepare some variables. The full Range represents the complete file.
Range full = new Range(0, length - 1, length);
List<Range> ranges = new ArrayList<>();
// Validate and process Range and If-Range headers.
String range = request.getHeader("Range");
if (range != null) {
// Range header should match format "bytes=n-n,n-n,n-n...". If not, then return 416.
if (!range.matches("^bytes=\\d*-\\d*(,\\d*-\\d*)*$")) {
response.setHeader("Content-Range", "bytes */" + length); // Required in 416.
response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
return;
}
String ifRange = request.getHeader("If-Range");
if (ifRange != null && !ifRange.equals(fileName)) {
try {
long ifRangeTime = request.getDateHeader("If-Range"); // Throws IAE if invalid.
if (ifRangeTime != -1) {
ranges.add(full);
}
} catch (IllegalArgumentException ignore) {
ranges.add(full);
}
}
// If any valid If-Range header, then process each part of byte range.
if (ranges.isEmpty()) {
for (String part : range.substring(6).split(",")) {
// Assuming a file with length of 100, the following examples returns bytes at:
// 50-80 (50 to 80), 40- (40 to length=100), -20 (length-20=80 to length=100).
long start = Range.sublong(part, 0, part.indexOf("-"));
long end = Range.sublong(part, part.indexOf("-") + 1, part.length());
if (start == -1) {
start = length - end;
end = length - 1;
} else if (end == -1 || end > length - 1) {
end = length - 1;
}
// Check if Range is syntactically valid. If not, then return 416.
if (start > end) {
response.setHeader("Content-Range", "bytes */" + length); // Required in 416.
response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
return;
}
// Add range.
ranges.add(new Range(start, end, length));
}
}
}
// Prepare and initialize response --------------------------------------------------------
// Get content type by file name and set content disposition.
String disposition = "inline";
// If content type is unknown, then set the default value.
// For all content types, see: http://www.w3schools.com/media/media_mimeref.asp
// To add new content types, add new mime-mapping entry in web.xml.
if (contentType == null) {
contentType = "application/octet-stream";
} else if (!contentType.startsWith("image")) {
// Else, expect for images, determine content disposition. If content type is supported by
// the browser, then set to inline, else attachment which will pop a 'save as' dialogue.
String accept = request.getHeader("Accept");
disposition = accept != null && HttpUtils.accepts(accept, contentType) ? "inline" : "attachment";
}
logger.debug("Content-Type : {}", contentType);
// Initialize response.
response.reset();
response.setBufferSize(DEFAULT_BUFFER_SIZE);
response.setHeader("Content-Type", contentType);
response.setHeader("Content-Disposition", disposition + ";filename=\"" + fileName + "\"");
logger.debug("Content-Disposition : {}", disposition);
response.setHeader("Accept-Ranges", "bytes");
response.setHeader("ETag", fileName);
response.setDateHeader("Last-Modified", lastModified);
response.setDateHeader("Expires", System.currentTimeMillis() + DEFAULT_EXPIRE_TIME);
// Send requested file (part(s)) to client ------------------------------------------------
// Prepare streams.
try (InputStream input = new BufferedInputStream(Files.newInputStream(filepath));
OutputStream output = response.getOutputStream()) {
if (ranges.isEmpty() || ranges.get(0) == full) {
// Return full file.
logger.info("Return full file");
response.setContentType(contentType);
response.setHeader("Content-Range", "bytes " + full.start + "-" + full.end + "/" + full.total);
response.setHeader("Content-Length", String.valueOf(full.length));
Range.copy(input, output, length, full.start, full.length);
} else if (ranges.size() == 1) {
// Return single part of file.
Range r = ranges.get(0);
logger.info("Return 1 part of file : from ({}) to ({})", r.start, r.end);
response.setContentType(contentType);
response.setHeader("Content-Range", "bytes " + r.start + "-" + r.end + "/" + r.total);
response.setHeader("Content-Length", String.valueOf(r.length));
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 206.
// Copy single part range.
Range.copy(input, output, length, r.start, r.length);
} else {
// Return multiple parts of file.
response.setContentType("multipart/byteranges; boundary=" + MULTIPART_BOUNDARY);
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 206.
// Cast back to ServletOutputStream to get the easy println methods.
ServletOutputStream sos = (ServletOutputStream) output;
// Copy multi part range.
for (Range r : ranges) {
logger.info("Return multi part of file : from ({}) to ({})", r.start, r.end);
// Add multipart boundary and header fields for every range.
sos.println();
sos.println("--" + MULTIPART_BOUNDARY);
sos.println("Content-Type: " + contentType);
sos.println("Content-Range: bytes " + r.start + "-" + r.end + "/" + r.total);
// Copy single part range of multi part range.
Range.copy(input, output, length, r.start, r.length);
}
// End with multipart boundary.
sos.println();
sos.println("--" + MULTIPART_BOUNDARY + "--");
}
}
}
private static class Range {
long start;
long end;
long length;
long total;
/**
* Construct a byte range.
* #param start Start of the byte range.
* #param end End of the byte range.
* #param total Total length of the byte source.
*/
public Range(long start, long end, long total) {
this.start = start;
this.end = end;
this.length = end - start + 1;
this.total = total;
}
public static long sublong(String value, int beginIndex, int endIndex) {
String substring = value.substring(beginIndex, endIndex);
return (substring.length() > 0) ? Long.parseLong(substring) : -1;
}
private static void copy(InputStream input, OutputStream output, long inputSize, long start, long length) throws IOException {
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
int read;
if (inputSize == length) {
// Write full range.
while ((read = input.read(buffer)) > 0) {
output.write(buffer, 0, read);
output.flush();
}
} else {
input.skip(start);
long toRead = length;
while ((read = input.read(buffer)) > 0) {
if ((toRead -= read) > 0) {
output.write(buffer, 0, read);
output.flush();
} else {
output.write(buffer, 0, (int) toRead + read);
output.flush();
break;
}
}
}
}
}
private static class HttpUtils {
/**
* Returns true if the given accept header accepts the given value.
* #param acceptHeader The accept header.
* #param toAccept The value to be accepted.
* #return True if the given accept header accepts the given value.
*/
public static boolean accepts(String acceptHeader, String toAccept) {
String[] acceptValues = acceptHeader.split("\\s*(,|;)\\s*");
Arrays.sort(acceptValues);
return Arrays.binarySearch(acceptValues, toAccept) > -1
|| Arrays.binarySearch(acceptValues, toAccept.replaceAll("/.*$", "/*")) > -1
|| Arrays.binarySearch(acceptValues, "*/*") > -1;
}
/**
* Returns true if the given match header matches the given value.
* #param matchHeader The match header.
* #param toMatch The value to be matched.
* #return True if the given match header matches the given value.
*/
public static boolean matches(String matchHeader, String toMatch) {
String[] matchValues = matchHeader.split("\\s*,\\s*");
Arrays.sort(matchValues);
return Arrays.binarySearch(matchValues, toMatch) > -1
|| Arrays.binarySearch(matchValues, "*") > -1;
}
}
}
Try this it will surely compatible with your case.
Thank you.
I am writing a java application to serve as a local proxy. I have been helped greatly by this piece of code from http://www.nsftools.com/tips/jProxy.java. See program below:
/* <!-- in case someone opens this in a browser... --> <pre> */
/*
* This is a simple multi-threaded Java proxy server
* for HTTP requests (HTTPS doesn't seem to work, because
* the CONNECT requests aren't always handled properly).
* I implemented the class as a thread so you can call it
* from other programs and kill it, if necessary (by using
* the closeSocket() method).
*
* We'll call this the 1.1 version of this class. All I
* changed was to separate the HTTP header elements with
* \r\n instead of just \n, to comply with the official
* HTTP specification.
*
* This can be used either as a direct proxy to other
* servers, or as a forwarding proxy to another proxy
* server. This makes it useful if you want to monitor
* traffic going to and from a proxy server (for example,
* you can run this on your local machine and set the
* fwdServer and fwdPort to a real proxy server, and then
* tell your browser to use "localhost" as the proxy, and
* you can watch the browser traffic going in and out).
*
* One limitation of this implementation is that it doesn't
* close the ProxyThread socket if the client disconnects
* or the server never responds, so you could end up with
* a bunch of loose threads running amuck and waiting for
* connections. As a band-aid, you can set the server socket
* to timeout after a certain amount of time (use the
* setTimeout() method in the ProxyThread class), although
* this can cause false timeouts if a remote server is simply
* slow to respond.
*
* Another thing is that it doesn't limit the number of
* socket threads it will create, so if you use this on a
* really busy machine that processed a bunch of requests,
* you may have problems. You should use thread pools if
* you're going to try something like this in a "real"
* application.
*
* Note that if you're using the "main" method to run this
* by itself and you don't need the debug output, it will
* run a bit faster if you pipe the std output to 'nul'.
*
* You may use this code as you wish, just don't pretend
* that you wrote it yourself, and don't hold me liable for
* anything that it does or doesn't do. If you're feeling
* especially honest, please include a link to nsftools.com
* along with the code. Thanks, and good luck.
*
* Julian Robichaux -- http://www.nsftools.com
*/
import java.io.*;
import java.net.*;
import java.lang.reflect.Array;
public class jProxy extends Thread
{
public static final int DEFAULT_PORT = 8080;
private ServerSocket server = null;
private int thisPort = DEFAULT_PORT;
private String fwdServer = "";
private int fwdPort = 0;
private int ptTimeout = ProxyThread.DEFAULT_TIMEOUT;
private int debugLevel = 0;
private PrintStream debugOut = System.out;
/* here's a main method, in case you want to run this by itself */
public static void main (String args[])
{
int port = 0;
String fwdProxyServer = "";
int fwdProxyPort = 0;
if (args.length == 0)
{
System.err.println("USAGE: java jProxy <port number> [<fwd proxy> <fwd port>]");
System.err.println(" <port number> the port this service listens on");
System.err.println(" <fwd proxy> optional proxy server to forward requests to");
System.err.println(" <fwd port> the port that the optional proxy server is on");
System.err.println("\nHINT: if you don't want to see all the debug information flying by,");
System.err.println("you can pipe the output to a file or to 'nul' using \">\". For example:");
System.err.println(" to send output to the file prox.txt: java jProxy 8080 > prox.txt");
System.err.println(" to make the output go away: java jProxy 8080 > nul");
return;
}
// get the command-line parameters
port = Integer.parseInt(args[0]);
if (args.length > 2)
{
fwdProxyServer = args[1];
fwdProxyPort = Integer.parseInt(args[2]);
}
// create and start the jProxy thread, using a 20 second timeout
// value to keep the threads from piling up too much
System.err.println(" ** Starting jProxy on port " + port + ". Press CTRL-C to end. **\n");
jProxy jp = new jProxy(port, fwdProxyServer, fwdProxyPort, 20);
jp.setDebug(1, System.out); // or set the debug level to 2 for tons of output
jp.start();
// run forever; if you were calling this class from another
// program and you wanted to stop the jProxy thread at some
// point, you could write a loop that waits for a certain
// condition and then calls jProxy.closeSocket() to kill
// the running jProxy thread
while (true)
{
try { Thread.sleep(3000); } catch (Exception e) {}
}
// if we ever had a condition that stopped the loop above,
// we'd want to do this to kill the running thread
//jp.closeSocket();
//return;
}
/* the proxy server just listens for connections and creates
* a new thread for each connection attempt (the ProxyThread
* class really does all the work)
*/
public jProxy (int port)
{
thisPort = port;
}
public jProxy (int port, String proxyServer, int proxyPort)
{
thisPort = port;
fwdServer = proxyServer;
fwdPort = proxyPort;
}
public jProxy (int port, String proxyServer, int proxyPort, int timeout)
{
thisPort = port;
fwdServer = proxyServer;
fwdPort = proxyPort;
ptTimeout = timeout;
}
/* allow the user to decide whether or not to send debug
* output to the console or some other PrintStream
*/
public void setDebug (int level, PrintStream out)
{
debugLevel = level;
debugOut = out;
}
/* get the port that we're supposed to be listening on
*/
public int getPort ()
{
return thisPort;
}
/* return whether or not the socket is currently open
*/
public boolean isRunning ()
{
if (server == null)
return false;
else
return true;
}
/* closeSocket will close the open ServerSocket; use this
* to halt a running jProxy thread
*/
public void closeSocket ()
{
try {
// close the open server socket
server.close();
// send it a message to make it stop waiting immediately
// (not really necessary)
/*Socket s = new Socket("localhost", thisPort);
OutputStream os = s.getOutputStream();
os.write((byte)0);
os.close();
s.close();*/
} catch(Exception e) {
if (debugLevel > 0)
debugOut.println(e);
}
server = null;
}
public void run()
{
try {
// create a server socket, and loop forever listening for
// client connections
server = new ServerSocket(thisPort);
if (debugLevel > 0)
debugOut.println("Started jProxy on port " + thisPort);
while (true)
{
Socket client = server.accept();
ProxyThread t = new ProxyThread(client, fwdServer, fwdPort);
t.setDebug(debugLevel, debugOut);
t.setTimeout(ptTimeout);
t.start();
}
} catch (Exception e) {
if (debugLevel > 0)
debugOut.println("jProxy Thread error: " + e);
}
closeSocket();
}
}
/*
* The ProxyThread will take an HTTP request from the client
* socket and send it to either the server that the client is
* trying to contact, or another proxy server
*/
class ProxyThread extends Thread
{
private Socket pSocket;
private String fwdServer = "";
private int fwdPort = 0;
private int debugLevel = 0;
private PrintStream debugOut = System.out;
// the socketTimeout is used to time out the connection to
// the remote server after a certain period of inactivity;
// the value is in milliseconds -- use zero if you don't want
// a timeout
public static final int DEFAULT_TIMEOUT = 20 * 1000;
private int socketTimeout = DEFAULT_TIMEOUT;
public ProxyThread(Socket s)
{
pSocket = s;
}
public ProxyThread(Socket s, String proxy, int port)
{
pSocket = s;
fwdServer = proxy;
fwdPort = port;
}
public void setTimeout (int timeout)
{
// assume that the user will pass the timeout value
// in seconds (because that's just more intuitive)
socketTimeout = timeout * 1000;
}
public void setDebug (int level, PrintStream out)
{
debugLevel = level;
debugOut = out;
}
public void run()
{
try
{
long startTime = System.currentTimeMillis();
// client streams (make sure you're using streams that use
// byte arrays, so things like GIF and JPEG files and file
// downloads will transfer properly)
BufferedInputStream clientIn = new BufferedInputStream(pSocket.getInputStream());
BufferedOutputStream clientOut = new BufferedOutputStream(pSocket.getOutputStream());
// the socket to the remote server
Socket server = null;
// other variables
byte[] request = null;
byte[] response = null;
int requestLength = 0;
int responseLength = 0;
int pos = -1;
StringBuffer host = new StringBuffer("");
String hostName = "";
int hostPort = 80;
// get the header info (the web browser won't disconnect after
// it's sent a request, so make sure the waitForDisconnect
// parameter is false)
request = getHTTPData(clientIn, host, false);
requestLength = Array.getLength(request);
// separate the host name from the host port, if necessary
// (like if it's "servername:8000")
hostName = host.toString();
pos = hostName.indexOf(":");
if (pos > 0)
{
try { hostPort = Integer.parseInt(hostName.substring(pos + 1));
} catch (Exception e) { }
hostName = hostName.substring(0, pos);
}
// either forward this request to another proxy server or
// send it straight to the Host
try
{
if ((fwdServer.length() > 0) && (fwdPort > 0))
{
server = new Socket(fwdServer, fwdPort);
} else {
server = new Socket(hostName, hostPort);
}
} catch (Exception e) {
// tell the client there was an error
String errMsg = "HTTP/1.0 500\nContent Type: text/plain\n\n" +
"Error connecting to the server:\n" + e + "\n";
clientOut.write(errMsg.getBytes(), 0, errMsg.length());
}
if (server != null)
{
server.setSoTimeout(socketTimeout);
BufferedInputStream serverIn = new BufferedInputStream(server.getInputStream());
BufferedOutputStream serverOut = new BufferedOutputStream(server.getOutputStream());
// send the request out
serverOut.write(request, 0, requestLength);
serverOut.flush();
// and get the response; if we're not at a debug level that
// requires us to return the data in the response, just stream
// it back to the client to save ourselves from having to
// create and destroy an unnecessary byte array. Also, we
// should set the waitForDisconnect parameter to 'true',
// because some servers (like Google) don't always set the
// Content-Length header field, so we have to listen until
// they decide to disconnect (or the connection times out).
if (debugLevel > 1)
{
response = getHTTPData(serverIn, true);
responseLength = Array.getLength(response);
} else {
responseLength = streamHTTPData(serverIn, clientOut, true);
}
serverIn.close();
serverOut.close();
}
// send the response back to the client, if we haven't already
if (debugLevel > 1)
clientOut.write(response, 0, responseLength);
// if the user wants debug info, send them debug info; however,
// keep in mind that because we're using threads, the output won't
// necessarily be synchronous
if (debugLevel > 0)
{
long endTime = System.currentTimeMillis();
debugOut.println("Request from " + pSocket.getInetAddress().getHostAddress() +
" on Port " + pSocket.getLocalPort() +
" to host " + hostName + ":" + hostPort +
"\n (" + requestLength + " bytes sent, " +
responseLength + " bytes returned, " +
Long.toString(endTime - startTime) + " ms elapsed)");
debugOut.flush();
}
if (debugLevel > 1)
{
debugOut.println("REQUEST:\n" + (new String(request)));
debugOut.println("RESPONSE:\n" + (new String(response)));
debugOut.flush();
}
// close all the client streams so we can listen again
clientOut.close();
clientIn.close();
pSocket.close();
} catch (Exception e) {
if (debugLevel > 0)
debugOut.println("Error in ProxyThread: " + e);
//e.printStackTrace();
}
}
private byte[] getHTTPData (InputStream in, boolean waitForDisconnect)
{
// get the HTTP data from an InputStream, and return it as
// a byte array
// the waitForDisconnect parameter tells us what to do in case
// the HTTP header doesn't specify the Content-Length of the
// transmission
StringBuffer foo = new StringBuffer("");
return getHTTPData(in, foo, waitForDisconnect);
}
private byte[] getHTTPData (InputStream in, StringBuffer host, boolean waitForDisconnect)
{
// get the HTTP data from an InputStream, and return it as
// a byte array, and also return the Host entry in the header,
// if it's specified -- note that we have to use a StringBuffer
// for the 'host' variable, because a String won't return any
// information when it's used as a parameter like that
ByteArrayOutputStream bs = new ByteArrayOutputStream();
streamHTTPData(in, bs, host, waitForDisconnect);
return bs.toByteArray();
}
private int streamHTTPData (InputStream in, OutputStream out, boolean waitForDisconnect)
{
StringBuffer foo = new StringBuffer("");
return streamHTTPData(in, out, foo, waitForDisconnect);
}
private int streamHTTPData (InputStream in, OutputStream out,
StringBuffer host, boolean waitForDisconnect)
{
// get the HTTP data from an InputStream, and send it to
// the designated OutputStream
StringBuffer header = new StringBuffer("");
String data = "";
int responseCode = 200;
int contentLength = 0;
int pos = -1;
int byteCount = 0;
try
{
// get the first line of the header, so we know the response code
data = readLine(in);
if (data != null)
{
header.append(data + "\r\n");
pos = data.indexOf(" ");
if ((data.toLowerCase().startsWith("http")) &&
(pos >= 0) && (data.indexOf(" ", pos+1) >= 0))
{
String rcString = data.substring(pos+1, data.indexOf(" ", pos+1));
try
{
responseCode = Integer.parseInt(rcString);
} catch (Exception e) {
if (debugLevel > 0)
debugOut.println("Error parsing response code " + rcString);
}
}
}
// get the rest of the header info
while ((data = readLine(in)) != null)
{
// the header ends at the first blank line
if (data.length() == 0)
break;
header.append(data + "\r\n");
// check for the Host header
pos = data.toLowerCase().indexOf("host:");
if (pos >= 0)
{
host.setLength(0);
host.append(data.substring(pos + 5).trim());
}
// check for the Content-Length header
pos = data.toLowerCase().indexOf("content-length:");
if (pos >= 0)
contentLength = Integer.parseInt(data.substring(pos + 15).trim());
}
// add a blank line to terminate the header info
header.append("\r\n");
// convert the header to a byte array, and write it to our stream
out.write(header.toString().getBytes(), 0, header.length());
// if the header indicated that this was not a 200 response,
// just return what we've got if there is no Content-Length,
// because we may not be getting anything else
if ((responseCode != 200) && (contentLength == 0))
{
out.flush();
return header.length();
}
// get the body, if any; we try to use the Content-Length header to
// determine how much data we're supposed to be getting, because
// sometimes the client/server won't disconnect after sending us
// information...
if (contentLength > 0)
waitForDisconnect = false;
if ((contentLength > 0) || (waitForDisconnect))
{
try {
byte[] buf = new byte[4096];
int bytesIn = 0;
while ( ((byteCount < contentLength) || (waitForDisconnect))
&& ((bytesIn = in.read(buf)) >= 0) )
{
out.write(buf, 0, bytesIn);
byteCount += bytesIn;
}
} catch (Exception e) {
String errMsg = "Error getting HTTP body: " + e;
if (debugLevel > 0)
debugOut.println(errMsg);
//bs.write(errMsg.getBytes(), 0, errMsg.length());
}
}
} catch (Exception e) {
if (debugLevel > 0)
debugOut.println("Error getting HTTP data: " + e);
}
//flush the OutputStream and return
try { out.flush(); } catch (Exception e) {}
return (header.length() + byteCount);
}
private String readLine (InputStream in)
{
// reads a line of text from an InputStream
StringBuffer data = new StringBuffer("");
int c;
try
{
// if we have nothing to read, just return null
in.mark(1);
if (in.read() == -1)
return null;
else
in.reset();
while ((c = in.read()) >= 0)
{
// check for an end-of-line character
if ((c == 0) || (c == 10) || (c == 13))
break;
else
data.append((char)c);
}
// deal with the case where the end-of-line terminator is \r\n
if (c == 13)
{
in.mark(1);
if (in.read() != 10)
in.reset();
}
} catch (Exception e) {
if (debugLevel > 0)
debugOut.println("Error getting header: " + e);
}
// and return what we have
return data.toString();
}
}
Problem is secure sites like "https://www.google.com" don't work. I have tried to tweak the code over and over again but all to no avail. I have gone through questions answered here and many more sites too but I just cant seem to get it to work. I would av posted links but I cant cos I dnt av enuf reputation yet.
Someone pls help me with what needs to be done to support Secure Sites(HTTPS).
Thanks in advance.
PS: Sorry if I didn't ask the question the right way.I'm a newbie. Cheers...
Default port for https is 443. This is used if no port is specified in the URL, as is the case with the google site you give as an example. Try adjusting the code with this in mind.
I ran into an issue trying to read from a DataOutputStream with a Scanner. The following code fails with an input mismatch exception.
DataOutputStream o = new DataOutputStream(new FileOutputStream("temp"));
o.writeByte(1);
o.flush(); o.close();
Scanner i = new Scanner(new FileInputStream("temp"));
System.out.println(i.nextByte());
i.close();
So what output streams ARE compatible with Scanner? And is there an Output/Input stream pair that are built for reading and writing all the primitive types and string lines? I really wanted a non-deprecated readLine() method, and all the input streams I've seen that have that method are deprecated.
EDIT:
The input and output streams will be across sockets for a Client/Server application. Here is the entire relevant code for each side:
SERVER
package server;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Scanner;
/**
* Server class handles all clients that wish to read a file stored on this
* server's
* database. Also connects to other servers to share some information.
*
* #author Josh Wilkins
*/
public class Server{
//Server message type constants
private static final byte REPLY = 0;
private static final byte YES = 1;
private static final byte NO = 2;
private static final byte UPDATE = 3;
private final int id;
private final ServerSocket fileServer; //connection point for clients
private HashMap<String, FileObject> files; //list of files in database, hashed by file name
/**
* Creates a new file server with given id on the specified port.
* <p>
* #param id
* #param port
* <p>
* #throws IOException
*/
public Server(int id, int port) throws IOException{
this.id = id;
fileServer = new ServerSocket(port);
files = new HashMap<>();
}
/**
* Loops forever, accepting new clients and creating threads to handle
* them.
* <p>
* #throws IOException
*/
public void acceptClients() throws IOException{
while( true ){
Socket client = fileServer.accept();
(new ClientHandler(client)).run();
}
}
/**
* Sends most updated version of specified file to specified server. Will
* only send update if the server should host the file. UNIMPLEMENTED.
* <p>
* #throws UnsupportedOperationException
*/
private void sendUpdate(int server, FileObject file){
//TODO
throw new UnsupportedOperationException("Not supported yet.");
}
/**
* Creates a new file in the database with the file name contained in the
* string file.
* <p>
* #param file the file name of the new file object
*/
private synchronized void createFileObject(String file){
//TODO: Reject files not hashed to this server
try{
files.put(file, new FileObject(file));
} catch(IOException ex){
System.err.println("Failed to create file: " + file);
ex.printStackTrace(System.err);
}
}
/**
* Determines if this server is allowed to initiate a write request for a
* file by giving the client the most current version number.
* <p>
* #param file the file name of the file in question
* <p>
* #return true if allowed to initiate, false otherwise
*/
private boolean canInitiate(String file){
int hash0 = file.hashCode() % 7;
int hash1 = (file.hashCode() + 1) % 7;
return id == hash0 || id == hash1;
}
/**
* Threaded class to handle all requests from a single client connection.
*/
class ClientHandler extends Thread{
//Client message type constants
private static final byte READ = 0;
private static final byte WRITE = 1;
private static final byte COMMIT = 2;
private static final byte ABORT = 3;
private Socket client;
private DataOutputStream out;
private Scanner in;
private HashMap<String, Integer> pendingUpdates;
/**
* Sets up data input and output streams from given client socket.
* <p>
* #param client the socket that this client is connected to
*/
public ClientHandler(Socket client) throws IOException{
this.client = client;
out = new DataOutputStream(client.getOutputStream());
in = new Scanner(client.getInputStream());
pendingUpdates = new HashMap<>();
}
/**
* Listens for messages coming from client until the connection is
* closed on the client side.
*/
#Override
public void run(){
//Loops until the client closes the connection, then hasNext returns false
while( in.hasNext() ){
//wait for the next message type to be written to the stream
byte msgType = in.nextByte();
switch( msgType ){
case READ:
parseRead();
break;
case WRITE:
parseWrite();
break;
case COMMIT:
parseCommit();
break;
case ABORT:
parseAbort();
break;
default:
}
}
//connection is no longer needed, try to clean everything up
try{
in.close();
out.close();
client.close();
} catch(IOException ex){
System.err.println("Failed to close client at " + client.getInetAddress());
ex.printStackTrace(System.err);
}
}
/**
* Parses read message from client by collecting data from the input
* stream and processing it. Assumes the message type byte has already
* been read. If the file exists in the database, its contents are
* written to the client, otherwise a null string is written.
*/
private void parseRead(){
//TODO: Reject files not hashed to this server
String file = in.nextLine().trim(); //get requested file name (should end with \n)
String contents = "";
if( files.containsKey(file) ){
try{
contents = files.get(file).getContents();
} catch(IOException ex){
System.err.println("Error reading from file: " + file);
ex.printStackTrace(System.err);
}
} else {
//TODO: Need to decide how to handle no such file
// - create file and return empty
// - just return empty, but don't create
// - return some error indicator (change file to "-1" or "File Not Found")
contents = "";
}
//send REPLY message to client
sendReply(file, contents);
}
/**
* Parses write message from client by collecting data form the input
* stream and processing it. Assumes the message type byte has already
* been read. If the file does not exist, one is created immediately
* regardless of further success. If the version number is viable, or
* if this server can initiate a write for the given file, the update is
* queued with the respective FileObject. The client is sent a YES or
* NO answer depending on success of queueing the update.
*/
private void parseWrite(){
//TODO: Reject files not hashed to this server
int version = in.nextInt(); //get version first
String file = in.nextLine().trim(); //get file name
String contents = in.useDelimiter("\\Z").next(); //read entire remaining stream for contents
boolean queued = false;
//Create file if it does not exist yet.
if( !files.containsKey(file) ){
createFileObject(file);
}
//queue update to file object
try{
//only queue if version is given or if this server can initiate
if( version > 0 ){
queued = files.get(file).queueUpdate(contents, version);
} else if( version < 0 && canInitiate(file) ){
queued = files.get(file).queueUpdate(contents);
version = files.get(file).getVersion() + 1;
}
} catch(IOException ex){
System.err.println("Failed to queue update to: " + file);
ex.printStackTrace(System.err);
}
//send response to client (positive if queued, negative if not)
if( queued ){
//TODO: What happens if an update is already queued?
pendingUpdates.put(file, version);
sendYes(version, file);
} else {
sendNo(file);
}
}
/**
* Parses commit message from client by collecting data form the input
* stream and processing it. Assumes the message type byte has already
* been read. If valid server id is read from the stream, an update
* message is sent to that server for the file. No response is sent to
* client.
*/
private void parseCommit(){
//TODO: Reject files not hashed to this server
int failed = in.nextInt();
String file = in.nextLine().trim();
//TODO: Handle improper commit (no pending update)
int version = pendingUpdates.remove(file);
try{
files.get(file).commitUpdate(version);
} catch(IOException ex){
System.err.println("Failed to commit: " + file + " v. " + version);
ex.printStackTrace(System.err);
}
if( failed >= 0 ){
sendUpdate(failed, files.get(file));
}
}
/**
* Parses abort message from client by collecting data form the input
* stream and processing it. Assumes the message type byte has already
* been read. Simply removes the pending update from the queues. No
* response is sent to the client.
*/
private void parseAbort(){
//TODO: Reject files not hashed to this server
String file = in.nextLine().trim();
int version = pendingUpdates.remove(file); //if no update is queued this simply returns null
//what happens if:
// Integer xObj = null;
// int x = xObj;
// print(x);
try{
files.get(file).abortUpdate(version);
} catch(IOException ex){
System.err.println("Failed to abort: " + file + " v. " + version);
ex.printStackTrace(System.err);
}
}
/**
* Sends reply message to client. Assumes partial failure is impossible.
* <p>
* #param file name of file
* #param contents data contained in file
*/
public void sendReply(String file, String contents){
try{
out.writeByte(REPLY);
out.writeChars(file + "\n"); //end file name with CR for easy reading
out.writeChars(contents);
} catch(IOException ex){
System.err.println("Error sending REPLY(" + file + ", <" + contents.length() + ">)");
ex.printStackTrace(System.err);
}
}
/**
* Sends yes message to client. Assumes partial failure is impossible.
* <p>
* #param version this updates version number
* #param file name of file
*/
public void sendYes(int version, String file){
try{
out.writeByte(YES);
out.writeInt(version);
out.writeChars(file + "\n");
} catch(IOException ex){
System.err.println("Error sending YES(" + version + ", " + file + ")");
ex.printStackTrace(System.err);
}
}
/**
* Sends reply message to client. Assumes partial failure is impossible.
* <p>
* #param file name of file
*/
public void sendNo(String file){
try{
out.writeByte(NO);
out.writeChars(file + "\n");
} catch(IOException ex){
System.err.println("Error sending NO(" + file + ")");
ex.printStackTrace(System.err);
}
}
}
}
CLIENT
import java.io.*;
import java.net.Socket;
import java.util.Random;
import java.util.Scanner;
/**
* Client will take as input the operations (WRITE or READ), the file it wishes to use,
* and the servers in the network. The client will attempted to connect to three servers
* when a WRITE is chosen and a random server for READ. The servers ranges for READ and WRITE
* are based on a hashed value of the file name + the next in the line.
* example: Sever 1 , Server 2 and Server 3;
*
* #author kattex
*/
public class Client {
private Socket socket = null;
private Scanner inputStream = null;
private DataOutputStream outputStream = null;
private boolean isConnected = false;
private int sequenceNumber = -1;
private int response = 0;
private byte READ = 0;
private byte WRITE = 1;
private byte COMMIT = 2;
private byte ABORT = 3;
private byte YES = 1;
private byte NO = 2;
private byte REPLY = 0;
/**
* Connect with server code running in local host or in any other host
*/
private void connect(String address, int port) {
try {
// socket = new Socket("localHost", 4445);
System.out.println(address + " " + port);
socket = new Socket(address, port);
//outputStream = new ObjectOutputStream(socket.getOutputStream());
outputStream = new DataOutputStream(socket.getOutputStream());
inputStream = new Scanner(socket.getInputStream());
isConnected = true;
} catch (IOException e) {
System.out.println("Unable to connect to server " + address);
}
}
/*
* Create a random integer within a min and max range
*/
public static int randInt(int min, int max, int count) {
Random rand = new Random();
System.out.println("randInt: " + min + "," + max + "," + count);
int randomNum = rand.nextInt((max - min) + 1) + min;
if (randomNum > count){
randomNum = randomNum % count;
if (randomNum < 0){
randomNum += count;
}
}
System.out.println("Random value: " + randomNum);
return randomNum;
}
/*
* Generate hash value for server numbers
*/
public static int hashFileName(String fileName, int serverCount){
int number = fileName.hashCode() % serverCount;
if (number < 0){
number += serverCount;
}
System.out.println("Hash Number: " + number);
return number;
}
/*
* Write out the contents to a file, if the file does not exist create it
*/
public static void CreateFile(String filename, String content){
try {
File file = new File(filename);
// if file doesnt exists, then create it
if (!file.exists()) {
file.createNewFile();
}
FileWriter fw = new FileWriter(file.getAbsoluteFile());
BufferedWriter bw = new BufferedWriter(fw);
bw.write(content);
bw.close();
System.out.println("Done");
} catch (IOException e) {
System.out.println("Erro creating file");
e.printStackTrace();
}
}
/*
* Find the next server in the list
*/
public static int nextServer(int number, int count){
int nextInt;
number++;
nextInt = number % count;
if (number < 0){
number += count;
}
return nextInt;
}
/*
* Send the WRITE messaget to server
*/
private void sendWrite(String obj){
try {
System.out.print("Pause...");
(new Scanner(System.in)).nextLine();
outputStream.writeByte(WRITE);
System.out.print("Pause...");
(new Scanner(System.in)).nextLine();
outputStream.writeInt(sequenceNumber);
System.out.print("Pause...");
(new Scanner(System.in)).nextLine();
outputStream.writeChars(obj);
outputStream.writeChar('\n');
System.out.print("Pause...");
(new Scanner(System.in)).nextLine();
String contents = readFile(obj);
System.out.println("Contents of file " + obj);
outputStream.writeChars(contents);
System.out.print("Pause...");
(new Scanner(System.in)).nextLine();
System.out.println("sending message to server");
int msgType = inputStream.nextByte();
if (msgType == YES){
sequenceNumber = inputStream.nextInt();
String tempFileName = inputStream.nextLine().trim();
response = 1;
System.out.println("Receved YES for file " + tempFileName);
}
if (msgType == NO){
String tempFileName = inputStream.nextLine();
System.out.println("Receved NO for file " + tempFileName);
}
} catch (IOException e) {
System.out.println("Error writing WRITE message to server");
e.printStackTrace(System.err);
}
}
/*
* Read the file into a string that can be sent throught a TCP socket
*/
public String readFile(String fileName) throws FileNotFoundException{
String output = new Scanner(new File(fileName)).useDelimiter("\\Z").next();
System.out.println("File output in READFile" + output);
return output;
}
/*
* Send Abort message to the server
*/
public void sendAbort(String obj){
try {
outputStream.writeByte(ABORT);
outputStream.writeChars(obj);
outputStream.writeChar('\n');
System.out.println("sending abort message to server");
} catch (IOException e) {
e.printStackTrace();
System.out.println("Error sending ABORT message to server");
}
}
/*
* Send commit to Server
*/
public void sendCommit(int Fsvr, String obj){
try {
outputStream.writeByte(COMMIT);
outputStream.writeInt(Fsvr);
outputStream.writeBytes(obj);
System.out.println("sending Commit message to server");
} catch (IOException e) {
e.printStackTrace();
System.out.println("Error sending COMMIT message to server");
}
}
/*
* Send READ request
*/
public void sendREAD(String obj){
String u;
try {
outputStream.writeByte(READ);
outputStream.writeChars(obj);
outputStream.writeChar('n');
System.out.println("sending READ Request message to server");
byte type = inputStream.nextByte();
//File fl = new File(obj);
if (type == REPLY){
String file = inputStream.nextLine().trim();
String contents = inputStream.useDelimiter("\\z").next();
CreateFile(file, contents);
}
} catch (IOException e) {
e.printStackTrace();
System.out.println("Erorro sedning READ Request message to server");
}
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
Client client = new Client();
int serverCount = 0;
int FSvr = -1;
int length = args.length;
String Object = "j.txt";
String type = "write";
int hashValue = hashFileName(Object, 7);
if (type.equals("write") ){
Client client2 = new Client();
Client client3 = new Client();
client.connect("localhost", 4445);
if (client.isConnected){
client.sendWrite(Object);
if (client.response == 1){
client2.sequenceNumber = client.sequenceNumber;
}
}
int nextValue = nextServer(hashValue,7);
//need to add commit message
int thirdValue = 3;
System.out.println("Server Numbers " + hashValue + " " + nextValue + " " + thirdValue);
serverCount = client.response + client2.response + client3.response;
System.out.println("Servercount " + serverCount);
if (serverCount >= 2){
if(client.response != 1 ){FSvr = hashValue; }
if(client2.response != 1){FSvr = nextValue; }
if(client3.response != 1){FSvr = thridValue;}
if(client.response == 1){client.sendCommit( FSvr,Object);}
if(client2.response == 1){client2.sendCommit(FSvr,Object);}
if(client3.response == 1){client3.sendCommit(FSvr,Object);}
}else{
if(client.response == 1 ){client.sendAbort(Object); }
if(client2.response == 1){client2.sendAbort(Object);}
if(client3.response == 1){client3.sendAbort(Object);}
}
} else {
if (type.equals("read")){
int RValue = randInt(hashValue, hashValue + 2, 7);
System.out.println("HashVlue: " + hashValue + " RValue: " + RValue);
client.sendREAD(Object);
}
}
}
}
Most of the message handling on the server side is in ClientHandler. I'm less familiar with the client side, as I didn't write it. Obviously with this implementation, there is an InputMismatchException when reading the byte msgType on the server side. I also realized while searching for the solution, that at the very least I should be adding white space between all the fields for Scanner to parse them separately, but I still have issues.
Use a DataOutputStream and a DataInputStream. Instead of lines, use writeUTF() and readUTF(). For efficiency, put buffered streams underneath, and flush the output before you read.
Scanner takes Input Streams such as System.in you are better off using BufferedReader here. E.g
BufferedReader reader = new BufferReader(new FileReader("temp"));
System.out.println(reader.readLine());
reader.close();
I am new to Java and at the moment lost.
I have this code:
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
/**
*
* #author Darwish
*/
public class M3UReader {
/**
* #param args the command line arguments
*/
public static boolean isValidHeader(String playList)
{
boolean returnValue = false;
BufferedReader br;
try
{
br = new BufferedReader(new FileReader(new File(playList)));
String s = br.readLine(); // declares the variable "s"
if(s.startsWith("#EXTM3U")) { // checks the line for this keyword
returnValue = true; // if its found, return true
}
br.close();
}
catch (Exception e)
{
System.err.println("isValidHeader:: error with file "+ playList + ": " + e.getMessage());
}
return returnValue;
}
public static int getNumberOfTracks(String playList)
{
int numberOfTracks = 0; // sets the default value to zero "0"
try
{
BufferedReader br = new BufferedReader(new FileReader(new File(playList)));
String s;
while((s = br.readLine())!=null) // if "s" first line is not null
{
if(s.startsWith("#")==false) { // if the first line starts with "#" equals to false.
numberOfTracks++; // increments
}
}
br.close();
}
catch (Exception e)
{
numberOfTracks = -1; // chek if the file doesnt exist
System.err.println("could not open/read line from/close filename "+ playList);
}
return numberOfTracks;
}
public static int getTotalMinutes(String playList)
{
// code needed here
}
public static void main(String[] args) {
// TODO code application logic here
String filename = "files\\playlist.m3u"; // finds the file to read (filename <- variable declaration.)
boolean isHeaderValid = M3UReader.isValidHeader(filename); // declares the variabe isHeaderValid and links it with the class isValidHeader
System.out.println(filename + "header tested as "+ isHeaderValid); // outputs the results
if(isHeaderValid)
{
int numOfTracks = M3UReader.getNumberOfTracks(filename);
System.out.println(filename + " has "+ numOfTracks + " tracks ");
}
}
}
On the method getTotalMinutes, I have to find a way to calculate the totals of the int values that was read from the file. The File has this data:
#EXTM3U
#EXTINF:537,Banco De Gaia - Drippy F:\SortedMusic\Electronic\Banco De Gaia\Big Men Cry\01 Drippy.mp3
#EXTINF:757,Banco De Gaia - Celestine F:\SortedMusic\Electronic\Banco De Gaia\Big Men Cry\02 Celestine.mp3
#EXTINF:565,Banco De Gaia - Drunk As A Monk F:\SortedMusic\Electronic\Banco De Gaia\Big Men Cry\03 Drunk As A Monk.mp3
#EXTINF:369,Banco De Gaia - Big Men Cry F:\SortedMusic\Electronic\Banco De Gaia\Big Men Cry\04 Big Men Cry.mp3
The number after the #EXTINF: is the length of the music which from the data above is in seconds.
I don't know what code to write on the getTotalMinutes method to get the program to read the minutes from the file and then calculate all of them to get the total of minutes. I searched the web on how to do this unfortunately I can't find any. So any help is appreciated.
You can use this, its just copy of your getNumberTracks method but it is parsing the file the way you need to get total minutes :
public static final String beginning = "#EXTINF:";
public static final String afterNumber = ",";
public static int getTotalMinutes(String playList) {
int value = 0;
try {
BufferedReader br = new BufferedReader(new FileReader(new File(playList)));
String s;
while ((s = br.readLine()) != null) // if "s" first line is not null
{
if (s.contains(beginning)) {
String numberInString = s.substring(beginning.length(), s.indexOf(afterNumber));
value += Integer.valueOf(numberInString);
}
}
br.close();
} catch (Exception e) {
}
return value;
}
So, based on the description provided from here, the numeric value is the number of seconds.
So, given a String in the format of #EXTINF:{d},{t} you should be able to use simple String manipulation to get the value out...
String text = "#EXTINF:537,Banco De Gaia - Drippy F:\\SortedMusic\\Electronic\\Banco De Gaia\\Big Men Cry\\01 Drippy.mp3";
String durationText = text.substring(text.indexOf(":") + 1, text.indexOf(","));
int durationSeconds = Integer.parseInt(durationText);
System.out.println(durationSeconds);
Which will print 537...
Next we just need to do some simple time arithmetic...
double seconds = durationSeconds;
int hours = (int)(seconds / (60 * 60));
seconds = seconds % (60 * 60);
int minutes = (int)(seconds / 60);
seconds = seconds % (60);
System.out.println(hours + ":" + minutes + ":" + NumberFormat.getNumberInstance().format(seconds));
Which prints 0:8:57 (or 8 minutes and 57 seconds)
To read M3U files you'll want to search for information about M3U parsers. There are already many efficient open source parsers available, but you will need to pay close attention to their licenses if you are planning on selling or distributing this.
M3u Parser looks like promising if you just want something quick and efficient.
M3u Parser
public static int getTotalMinutes(String filename) {
int totalSeconds = 0;
if (isValidHeader(filename)) {
try (BufferedReader br = new BufferedReader(new FileReader(new File(filename)));) {
String nextLine;
while ((nextLine = br.readLine()) != null) {
//If the next line is metadata it should be possible to extract the length of the song
if (nextLine.startsWith(M3U_METADATA)) {
int i1 = nextLine.indexOf(":");
int i2 = nextLine.indexOf(",");
String substr = nextLine.substring(i1 + 1, i2);
totalSeconds += Integer.parseInt(substr);
}
}
} catch (IOException | NumberFormatException e) {
//Exception caught - set totalSeconds to 0
System.err.println("getTotalSeconds:: error with file " + filename + ": " + e.getMessage());
totalSeconds = 0;
}
}
return totalSeconds;
}
I'm currently using Win32ShellFolderManager2 and ShellFolder.getLinkLocation to resolve windows shortcuts in Java. Unfortunately, if the Java program is running as a service under Vista, getLinkLocation, this does not work. Specifically, I get an exception stating "Could not get shell folder ID list".
Searching the web does turn up mentions of this error message, but always in connection with JFileChooser. I'm not using JFileChooser, I just need to resolve a .lnk file to its destination.
Does anyone know of a 3rd-party parser for .lnk files written in Java I could use?
I've since found unofficial documentation for the .lnk format here, but I'd rather not have to do the work if anyone has done it before, since the format is rather scary.
Added comments (some explanation as well as credit to each contributor so far),additional check on the file magic, a quick test to see if a given file might be a valid link (without reading all of the bytes), a fix to throw a ParseException with appropriate message instead of ArrayIndexOutOfBoundsException if the file is too small, did some general clean-up.
Source here (if you have any changes, push them right to the GitHub repo/project.
package org.stackoverflowusers.file;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
/**
* Represents a Windows shortcut (typically visible to Java only as a '.lnk' file).
*
* Retrieved 2011-09-23 from http://stackoverflow.com/questions/309495/windows-shortcut-lnk-parser-in-java/672775#672775
* Originally called LnkParser
*
* Written by: (the stack overflow users, obviously!)
* Apache Commons VFS dependency removed by crysxd (why were we using that!?) https://github.com/crysxd
* Headerified, refactored and commented by Code Bling http://stackoverflow.com/users/675721/code-bling
* Network file support added by Stefan Cordes http://stackoverflow.com/users/81330/stefan-cordes
* Adapted by Sam Brightman http://stackoverflow.com/users/2492/sam-brightman
* Based on information in 'The Windows Shortcut File Format' by Jesse Hager <jessehager#iname.com>
* And somewhat based on code from the book 'Swing Hacks: Tips and Tools for Killer GUIs'
* by Joshua Marinacci and Chris Adamson
* ISBN: 0-596-00907-0
* http://www.oreilly.com/catalog/swinghks/
*/
public class WindowsShortcut
{
private boolean isDirectory;
private boolean isLocal;
private String real_file;
/**
* Provides a quick test to see if this could be a valid link !
* If you try to instantiate a new WindowShortcut and the link is not valid,
* Exceptions may be thrown and Exceptions are extremely slow to generate,
* therefore any code needing to loop through several files should first check this.
*
* #param file the potential link
* #return true if may be a link, false otherwise
* #throws IOException if an IOException is thrown while reading from the file
*/
public static boolean isPotentialValidLink(File file) throws IOException {
final int minimum_length = 0x64;
InputStream fis = new FileInputStream(file);
boolean isPotentiallyValid = false;
try {
isPotentiallyValid = file.isFile()
&& file.getName().toLowerCase().endsWith(".lnk")
&& fis.available() >= minimum_length
&& isMagicPresent(getBytes(fis, 32));
} finally {
fis.close();
}
return isPotentiallyValid;
}
public WindowsShortcut(File file) throws IOException, ParseException {
InputStream in = new FileInputStream(file);
try {
parseLink(getBytes(in));
} finally {
in.close();
}
}
/**
* #return the name of the filesystem object pointed to by this shortcut
*/
public String getRealFilename() {
return real_file;
}
/**
* Tests if the shortcut points to a local resource.
* #return true if the 'local' bit is set in this shortcut, false otherwise
*/
public boolean isLocal() {
return isLocal;
}
/**
* Tests if the shortcut points to a directory.
* #return true if the 'directory' bit is set in this shortcut, false otherwise
*/
public boolean isDirectory() {
return isDirectory;
}
/**
* Gets all the bytes from an InputStream
* #param in the InputStream from which to read bytes
* #return array of all the bytes contained in 'in'
* #throws IOException if an IOException is encountered while reading the data from the InputStream
*/
private static byte[] getBytes(InputStream in) throws IOException {
return getBytes(in, null);
}
/**
* Gets up to max bytes from an InputStream
* #param in the InputStream from which to read bytes
* #param max maximum number of bytes to read
* #return array of all the bytes contained in 'in'
* #throws IOException if an IOException is encountered while reading the data from the InputStream
*/
private static byte[] getBytes(InputStream in, Integer max) throws IOException {
// read the entire file into a byte buffer
ByteArrayOutputStream bout = new ByteArrayOutputStream();
byte[] buff = new byte[256];
while (max == null || max > 0) {
int n = in.read(buff);
if (n == -1) {
break;
}
bout.write(buff, 0, n);
if (max != null)
max -= n;
}
in.close();
return bout.toByteArray();
}
private static boolean isMagicPresent(byte[] link) {
final int magic = 0x0000004C;
final int magic_offset = 0x00;
return link.length >= 32 && bytesToDword(link, magic_offset) == magic;
}
/**
* Gobbles up link data by parsing it and storing info in member fields
* #param link all the bytes from the .lnk file
*/
private void parseLink(byte[] link) throws ParseException {
try {
if (!isMagicPresent(link))
throw new ParseException("Invalid shortcut; magic is missing", 0);
// get the flags byte
byte flags = link[0x14];
// get the file attributes byte
final int file_atts_offset = 0x18;
byte file_atts = link[file_atts_offset];
byte is_dir_mask = (byte)0x10;
if ((file_atts & is_dir_mask) > 0) {
isDirectory = true;
} else {
isDirectory = false;
}
// if the shell settings are present, skip them
final int shell_offset = 0x4c;
final byte has_shell_mask = (byte)0x01;
int shell_len = 0;
if ((flags & has_shell_mask) > 0) {
// the plus 2 accounts for the length marker itself
shell_len = bytesToWord(link, shell_offset) + 2;
}
// get to the file settings
int file_start = 0x4c + shell_len;
final int file_location_info_flag_offset_offset = 0x08;
int file_location_info_flag = link[file_start + file_location_info_flag_offset_offset];
isLocal = (file_location_info_flag & 2) == 0;
// get the local volume and local system values
//final int localVolumeTable_offset_offset = 0x0C;
final int basename_offset_offset = 0x10;
final int networkVolumeTable_offset_offset = 0x14;
final int finalname_offset_offset = 0x18;
int finalname_offset = link[file_start + finalname_offset_offset] + file_start;
String finalname = getNullDelimitedString(link, finalname_offset);
if (isLocal) {
int basename_offset = link[file_start + basename_offset_offset] + file_start;
String basename = getNullDelimitedString(link, basename_offset);
real_file = basename + finalname;
} else {
int networkVolumeTable_offset = link[file_start + networkVolumeTable_offset_offset] + file_start;
int shareName_offset_offset = 0x08;
int shareName_offset = link[networkVolumeTable_offset + shareName_offset_offset]
+ networkVolumeTable_offset;
String shareName = getNullDelimitedString(link, shareName_offset);
real_file = shareName + "\\" + finalname;
}
} catch (ArrayIndexOutOfBoundsException e) {
throw new ParseException("Could not be parsed, probably not a valid WindowsShortcut", 0);
}
}
private static String getNullDelimitedString(byte[] bytes, int off) {
int len = 0;
// count bytes until the null character (0)
while (true) {
if (bytes[off + len] == 0) {
break;
}
len++;
}
return new String(bytes, off, len);
}
/*
* convert two bytes into a short note, this is little endian because it's
* for an Intel only OS.
*/
private static int bytesToWord(byte[] bytes, int off) {
return ((bytes[off + 1] & 0xff) << 8) | (bytes[off] & 0xff);
}
private static int bytesToDword(byte[] bytes, int off) {
return (bytesToWord(bytes, off + 2) << 16) | bytesToWord(bytes, off);
}
}
Sam Brightman's solution is for local files only.
I added support for Network files:
Windows shortcut (.lnk) parser in Java?
http://code.google.com/p/8bits/downloads/detail?name=The_Windows_Shortcut_File_Format.pdf
http://www.javafaq.nu/java-example-code-468.html
public class LnkParser {
public LnkParser(File f) throws IOException {
parse(f);
}
private boolean isDirectory;
private boolean isLocal;
public boolean isDirectory() {
return isDirectory;
}
private String real_file;
public String getRealFilename() {
return real_file;
}
private void parse(File f) throws IOException {
// read the entire file into a byte buffer
FileInputStream fin = new FileInputStream(f);
ByteArrayOutputStream bout = new ByteArrayOutputStream();
byte[] buff = new byte[256];
while (true) {
int n = fin.read(buff);
if (n == -1) {
break;
}
bout.write(buff, 0, n);
}
fin.close();
byte[] link = bout.toByteArray();
parseLink(link);
}
private void parseLink(byte[] link) {
// get the flags byte
byte flags = link[0x14];
// get the file attributes byte
final int file_atts_offset = 0x18;
byte file_atts = link[file_atts_offset];
byte is_dir_mask = (byte)0x10;
if ((file_atts & is_dir_mask) > 0) {
isDirectory = true;
} else {
isDirectory = false;
}
// if the shell settings are present, skip them
final int shell_offset = 0x4c;
final byte has_shell_mask = (byte)0x01;
int shell_len = 0;
if ((flags & has_shell_mask) > 0) {
// the plus 2 accounts for the length marker itself
shell_len = bytes2short(link, shell_offset) + 2;
}
// get to the file settings
int file_start = 0x4c + shell_len;
final int file_location_info_flag_offset_offset = 0x08;
int file_location_info_flag = link[file_start + file_location_info_flag_offset_offset];
isLocal = (file_location_info_flag & 2) == 0;
// get the local volume and local system values
//final int localVolumeTable_offset_offset = 0x0C;
final int basename_offset_offset = 0x10;
final int networkVolumeTable_offset_offset = 0x14;
final int finalname_offset_offset = 0x18;
int finalname_offset = link[file_start + finalname_offset_offset] + file_start;
String finalname = getNullDelimitedString(link, finalname_offset);
if (isLocal) {
int basename_offset = link[file_start + basename_offset_offset] + file_start;
String basename = getNullDelimitedString(link, basename_offset);
real_file = basename + finalname;
} else {
int networkVolumeTable_offset = link[file_start + networkVolumeTable_offset_offset] + file_start;
int shareName_offset_offset = 0x08;
int shareName_offset = link[networkVolumeTable_offset + shareName_offset_offset]
+ networkVolumeTable_offset;
String shareName = getNullDelimitedString(link, shareName_offset);
real_file = shareName + "\\" + finalname;
}
}
private static String getNullDelimitedString(byte[] bytes, int off) {
int len = 0;
// count bytes until the null character (0)
while (true) {
if (bytes[off + len] == 0) {
break;
}
len++;
}
return new String(bytes, off, len);
}
/*
* convert two bytes into a short note, this is little endian because it's
* for an Intel only OS.
*/
private static int bytes2short(byte[] bytes, int off) {
return ((bytes[off + 1] & 0xff) << 8) | (bytes[off] & 0xff);
}
/**
* Returns the value of the instance variable 'isLocal'.
*
* #return Returns the isLocal.
*/
public boolean isLocal() {
return isLocal;
}
}
I can recommend this repository on GitHub:
https://github.com/BlackOverlord666/mslinks
There I've found a simple solution to create shortcuts:
ShellLink.createLink("path/to/existing/file.txt", "path/to/the/future/shortcut.lnk");
If you want to read shortcuts:
File shortcut = ...;
String pathToExistingFile = new ShellLink(shortcut).resolveTarget();
If you want to change the icon of the shortcut, use:
ShellLink sl = ...;
sl.setIconLocation("/path/to/icon/file");
You can edit most properties of the shortcutlink such as working directory, tooltip text, icon, command line arguments, hotkeys, create links to LAN shared files and directories and much more...
Hope this helps you :)
Kind regards
Josua Frank
The code plan9assembler linked to appears to work with minor modification. I think it's just the "& 0xff" to prevent sign extension when bytes are upcast to ints in the bytes2short function that need changing. I've added the functionality described in http://www.i2s-lab.com/Papers/The_Windows_Shortcut_File_Format.pdf to concatenate the "final part of the pathname" even though in practice this doesn't seem to be used in my examples. I've not added any error checking to the header or dealt with network shares. Here's what I'm using now:
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.text.DecimalFormat;
import java.text.NumberFormat;
public class LnkParser {
public LnkParser(File f) throws Exception {
parse(f);
}
private boolean is_dir;
public boolean isDirectory() {
return is_dir;
}
private String real_file;
public String getRealFilename() {
return real_file;
}
private void parse(File f) throws Exception {
// read the entire file into a byte buffer
FileInputStream fin = new FileInputStream(f);
ByteArrayOutputStream bout = new ByteArrayOutputStream();
byte[] buff = new byte[256];
while (true) {
int n = fin.read(buff);
if (n == -1) {
break;
}
bout.write(buff, 0, n);
}
fin.close();
byte[] link = bout.toByteArray();
// get the flags byte
byte flags = link[0x14];
// get the file attributes byte
final int file_atts_offset = 0x18;
byte file_atts = link[file_atts_offset];
byte is_dir_mask = (byte) 0x10;
if ((file_atts & is_dir_mask) > 0) {
is_dir = true;
} else {
is_dir = false;
}
// if the shell settings are present, skip them
final int shell_offset = 0x4c;
final byte has_shell_mask = (byte) 0x01;
int shell_len = 0;
if ((flags & has_shell_mask) > 0) {
// the plus 2 accounts for the length marker itself
shell_len = bytes2short(link, shell_offset) + 2;
}
// get to the file settings
int file_start = 0x4c + shell_len;
// get the local volume and local system values
final int basename_offset_offset = 0x10;
final int finalname_offset_offset = 0x18;
int basename_offset = link[file_start + basename_offset_offset]
+ file_start;
int finalname_offset = link[file_start + finalname_offset_offset]
+ file_start;
String basename = getNullDelimitedString(link, basename_offset);
String finalname = getNullDelimitedString(link, finalname_offset);
real_file = basename + finalname;
}
private static String getNullDelimitedString(byte[] bytes, int off) {
int len = 0;
// count bytes until the null character (0)
while (true) {
if (bytes[off + len] == 0) {
break;
}
len++;
}
return new String(bytes, off, len);
}
/*
* convert two bytes into a short note, this is little endian because it's
* for an Intel only OS.
*/
private static int bytes2short(byte[] bytes, int off) {
return ((bytes[off + 1] & 0xff) << 8) | (bytes[off] & 0xff);
}
}
I've also worked( now have no time for that) on '.lnk' in Java. My code is here
It's little messy( some testing trash) but local and network parsing works good. Creating links is implemented too. Please test and send me patches.
Parsing example:
Shortcut scut = Shortcut.loadShortcut(new File("C:\\t.lnk"));
System.out.println(scut.toString());
Creating new link:
Shortcut scut = new Shortcut(new File("C:\\temp"));
OutputStream os = new FileOutputStream("C:\\t.lnk");
os.write(scut.getBytes());
os.flush();
os.close();
The solution of #Code Bling does not work for me for Files in the User directory.
For Example "C:/Users/Username/Filename.txt".
The reason for that is: in The_Windows_Shortcut_File_Format.pdf
that was mentioned by #Stefan Cordes on page 6 it says that only the first 2 bits are important for volumes info.
All other bits might be filled with random garbage when the first bit of volumes info is "0".
So if it comes to:
isLocal = (file_location_info_flag & 2) == 0;
then file_location_info_flag might be "3".
This file is still local but this line of code assigns false to isLocal.
So i suggest the following adjustment to #Code Bling's code:
isLocal = (file_location_info_flag & 1) == 1;
This short code is really usefull...
But two fixes are needed:
the isPotentialValidLink improved not to load file if name doesn't end with ".lnk"
public static boolean isPotentialValidLink(final File file) {
final int minimum_length = 0x64;
boolean isPotentiallyValid = false;
if (file.getName().toLowerCase().endsWith(".lnk"))
try (final InputStream fis = new FileInputStream(file)) {
isPotentiallyValid = file.isFile() && fis.available() >= minimum_length && isMagicPresent(getBytes(fis, 32));
} catch (Exception e) {
// forget it
}
return isPotentiallyValid;
}
the offset has to be computed with 32bits not only a byte...
final int finalname_offset = bytesToDword(link,file_start + finalname_offset_offset) + file_start;
final int basename_offset = bytesToDword(link,file_start + basename_offset_offset) + file_start;
I found other non-professional technique. getting my job done.
File file=new File("C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\TeamViewer.lnk");//shortcut link
FileInputStream stream=new FileInputStream(file);
DataInputStream st=new DataInputStream(stream);
byte[] bytes=new byte[(int)stream.getChannel().size()];
stream.read(bytes);
String data=new String(bytes);
int i1=data.indexOf("C:\\Program Files");
int i2=data.indexOf(".exe",i1);
System.out.println(data.substring(i1, i2+4));
The given code works well, but has a bug. A java byte is a signed value from -128 to 127. We want an unsigned value from 0 to 255 to get the correct results. Just change the bytes2short function as follows:
static int bytes2short(byte[] bytes, int off) {
int low = (bytes[off]<0 ? bytes[off]+256 : bytes[off]);
int high = (bytes[off+1]<0 ? bytes[off+1]+256 : bytes[off+1])<<8;
return 0 | low | high;
}