I have an application written in JavaFX to control some lights in a theater with a very simple interface. Basically two buttons, one to fade lights up over 3 seconds and the other to fade them down over 3 seconds. The app connects to an Ethernet to Serial Server (Sealevel Sealink 4104) to control the lights.
I would like to add a browser interface so that the app can be controlled via any mobile device. I have added a Java web server based on code I got from this video.
https://www.youtube.com/watch?v=G4Z2PQfOHdY
The app runs, and I can get the web page I am looking for in the browser. However, my app interface never shows up. The idea is that the app interface is always present to indicate that it is running. The web page interface would be available to extend the control options to a mobile device.
The main question at this point is how do I get the web server to run in the background without interfering with the functioning of the app interface?
The web server code:
package lightcontrol2;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.StringTokenizer;
public final class JavaWebserver {
public final void StartServer() throws Exception {
// Set port number.
int port = 9000;
// Establish the listening socket.
ServerSocket serverSocket = new ServerSocket(port);
// Process HTTP sevice requests in an infinite loop.
while (true) {
// Listen for TCP connection request.
Socket connectionSocket = serverSocket.accept();
// Construct an object to process the HTTP request message.
HttpRequest request = new HttpRequest(connectionSocket);
// Create a new thread to process the request.
Thread thread = new Thread(request);
// Start the thread.
thread.start();
}
}
}
final class HttpRequest implements Runnable {
// Return carriage return (CR) and line feed (LF).
final static String CRLF = "\r\n";
Socket socket;
// Constructor.
public HttpRequest(Socket socket) throws Exception {
this.socket = socket;
}
// Implement the run() method of the Runnable interface.
// Within run(), explicitly catch and handle exceptions
// with try/ catch block.
#Override
public void run() {
try {
processRequest();
} catch (Exception e){
System.out.println(e);
}
}
private void processRequest() throws Exception {
// Get a reference to the socket's input and output streams.
InputStream instream = socket.getInputStream();
DataOutputStream os = new DataOutputStream(socket.getOutputStream());
// Set up input stream filters.
// Page 169, 10th line down or so . . .
// Reads the input data.
BufferedReader br = new BufferedReader(new InputStreamReader(instream));
// Get the request line of the HTTP request message.
// Get path/file.html version of http
String requestLine = br.readLine();
// Display the request line.
System.out.println();
System.out.println(requestLine);
// Deal with the request.
// Extract the filename from the request line.
// This is an input method with deliminators.
StringTokenizer tokens = new StringTokenizer(requestLine);
// Skip over the method, which should be 'GET'.
tokens.nextToken();
String fileName = tokens.nextToken();
// Root of the server.
String root = "/www/";
fileName = root + fileName;
// Open the requested file.
FileInputStream fis = null;
boolean fileExists = true;
try {
fis = new FileInputStream(fileName);
} catch (FileNotFoundException e) {
fileExists = false;
}
// Construct the response message.
String statusLine = null;
String contentTypeLine = null;
String entityBody = null;
if (fileExists) {
statusLine = "HTTP/1.0 200 OK" + CRLF;
contentTypeLine = "Content-type: " + contentType(fileName) + CRLF;
}
else {
statusLine = "HTTP/1.0 404 Not Found" + CRLF;
contentTypeLine = "Content-type: " + "text/html" + CRLF;
entityBody = "<HTML>" +
"<HEAD><TITLE>Not Found</TITLE></HEAD>" +
"<BODY>NOt Found</BODY></HTML>";
}
//Send the status line.
os.writeBytes(statusLine);
// Sent the content type line.
os.writeBytes(contentTypeLine);
// Send a blank line to indicate the end of the header lines.
os.writeBytes(CRLF);
// Send the entity body.
if (fileExists) {
sendBytes(fis, os);
os.writeBytes(statusLine);
fis.close();
} else {
os.writeBytes(statusLine);
os.writeBytes(entityBody);
os.writeBytes(contentTypeLine);
}
System.out.println("*****");
System.out.println(fileName);
System.out.println("*****");
// Get and display the header lines.
String headerLine = null;
while ((headerLine = br.readLine()).length() != 0) {
System.out.println(headerLine);
}
// Close streams and socket.
os.close();
br.close();
socket.close();
}
private static String contentType(String fileName) {
if (fileName.endsWith(".htm") || fileName.endsWith(".html")) {
return "text/html";
}
if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg")) {
return "image/jpeg";
}
if (fileName.endsWith(".gif")) {
return "image/gif";
}
return "application/octet-stream";
}
private static void sendBytes(FileInputStream fis, OutputStream os) throws Exception {
// Construct 1K buffer to hold bytes on way to the socket.
byte[] buffer = new byte[1024];
int bytes = 0;
// Copy requested file into the socket's output stream.
// read() returns -1, indicating end of file.
while ((bytes = fis.read(buffer)) != -1) {
os.write(buffer, 0, bytes);
}
}
}
Here is the interface code:
package lightcontrol2;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class LightControl2 extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
GridPane grid = createGrid();
SealinkConnect connect = new SealinkConnect();
JavaWebserver webserver = new JavaWebserver();
Button btnOn = new Button();
grid.add(btnOn, 0, 1);
btnOn.setText("3 Sec On");
btnOn.setOnAction((ActionEvent event) -> {
System.out.println("3N:100:A");
connect.sendCommand("3N:100:A");
});
Button btnOff = new Button();
grid.add(btnOff, 0, 2);
btnOff.setText("3 Sec Off");
btnOff.setOnAction((ActionEvent event) -> {
System.out.println("3F:A");
connect.sendCommand("3F:A");
});
BorderPane root = new BorderPane();
root.setPadding(new Insets(10));
root.setCenter(grid);
Scene scene = new Scene(root, 365, 300);
primaryStage.setTitle("Light Control Test");
primaryStage.setScene(scene);
scene.getStylesheets().add
(LightControl2.class.getResource("style.css").toExternalForm());
primaryStage.show();
connect.socketConnect();
webserver.StartServer();
}
private GridPane createGrid() {
GridPane grid = new GridPane();
grid.setAlignment(Pos.CENTER);
grid.setHgap(5);
grid.setVgap(10);
grid.setPadding(new Insets(10));
return grid;
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
I'm going to guess that JavaFX needs its thread back. It invokes start(), in which you call webserver.StartServer(), which in turns remains stuck in an infinite while(true) loop. You should do the socket accepting loop in a separate thread as well (and shut it down properly as needed) and let the start method return.
That being said, I would not recommend trying to implement a pseudo-HTTP-server on your own - that's just extra code, work and maintenance and might break in various ways if it is not RFC-compliant. There are plenty of embeddable lightweight HTTP servers you can use. As the author of JLHTTP, I think it could be a good match for your use case, but there are many others to choose from.
Using JLHTTP 2.1, you'd need something like this:
public void startWebServer() {
String dir = "."; // local folder to serve website files from
HTTPServer server = new HTTPServer(9000); // pick a port, any port
HTTPServer.VirtualHost host = server.getVirtualHost(null); // default virtual host
host.addContext("/", new FileContextHandler(new File(dir), "/")); // serve website files from disk directory
host.addContext("/api/lights", (request, response) -> {
Map<String, String> params = request.getParams();
String action = params.get("action");
if (action == null)
action = "";
switch (action) {
case "on":
connect.sendCommand("3N:100:A");
return 200; // ok
case "off":
connect.sendCommand("3F:A");
return 200; // ok
default:
return 400; // bad request
}
}, "GET", "POST"); // support both GET and POST requests
server.start();
}
Notes:
The website files (html/js/css/imags etc.) are served from disk - the example uses the current directory, but you should change that to a dedicated directory to prevent unintended access to sensitive files.
Your client code can use either a POST or GET request, via a form, AJAX, url with query parameters, etc., as long as it sends the appropriate action parameter and value.
You should also properly close the application, connection, HTTPServer etc.
This example accepts a single on/off action parameter. If you need more flexibility on the client side, you can pass individual command/device/value parameters and create the light controller command string in the context handler.
After you get everything working, you should consider security as well, or some kid in the audience will start messing with your show :-)
Related
I want to play video replay from low-end surveillance camera. Replays are saved on the camera in .mp4 format, with moov atom at the end. It's possible to retrieve file via http request using digset authentication. Approximate size of each video file is 20 MB, but download speed is only 3 Mbps, so downloading whole file takes about 60 s. This is to long, so I want to start displaying video before whole file will be downloaded.
Web browsers handles this kind of problem by reading end of file at the begining. I want to achieve same goal using c# and libvlcsharp, so created HttpMediaInput class.
public class HttpMediaInput : MediaInput
{
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
private HttpClientHandler _handler;
private HttpClient _httpClient;
private string _url;
Stream _stream = null;
public HttpMediaInput(string url, string username, string password)
{
_url = url;
_handler = new HttpClientHandler() { Credentials = new NetworkCredential(username, password) };
_httpClient = new HttpClient(_handler);
}
public override bool Open(out ulong size)
{
size = ulong.MaxValue;
try
{
_stream = _httpClient.GetStreamAsync(_url).Result;
base.CanSeek = _stream.CanSeek;
return true;
}
catch (Exception ex)
{
logger.Error(ex, $"Exception occurred during sending stream request to url: {_url}");
return false;
}
}
public unsafe override int Read(IntPtr buf, uint len)
{
try
{
byte[] buffer = new byte[len];
int bytesReaded = _stream.Read(buffer, 0, buffer.Length);
logger.Trace($"Bytes readed: {bytesReaded}");
Span<byte> byteSpan = new Span<byte>(buf.ToPointer(), buffer.Length);
buffer.CopyTo(byteSpan);
return bytesReaded;
}
catch (Exception ex)
{
logger.Error(ex, "Stream read exception");
return -1;
}
}
...
}
It works great for mp4 files that have all necessary metadata stored on the beginning, but no video is displayed in case of my camera.
Assuming that I will be able to download moov atom from mp4 using http range requests, how to provide this data to libvlc? Is it even possible?
I'm developing application using C#, WPF, dotnet framework.
VLC cannot play files from camera because http digest auth with md5 is considered to be deprecated (related issue in VLC repo).
However, I was able to resolve this problem following cube45 suggestions, I implemented range requests.
public override bool Open(out ulong size)
{
size = ulong.MaxValue;
try
{
HttpRequestMessage requestMessage = new HttpRequestMessage { RequestUri = new Uri(_url) };
requestMessage.Headers.Range = new System.Net.Http.Headers.RangeHeaderValue();
requestMessage.Method = HttpMethod.Head;
var response = _httpClient.SendAsync(requestMessage).Result;
size = (ulong)response.Content.Headers.ContentLength;
_fileSize = size;
logger.Trace($"Received content lenght | {size}");
base.CanSeek = true;
return true;
}
catch (Exception ex)
{
logger.Error(ex, $"Exception occurred during sending head request to url: {_url}");
return false;
}
}
public unsafe override int Read(IntPtr buf, uint len)
{
try
{
HttpRequestMessage requestMessage = new HttpRequestMessage { RequestUri = new Uri(_url) };
long startReadPosition = (long)_currentPosition;
long stopReadPosition = (long)_currentPosition + ((long)_numberOfBytesToReadInOneRequest - 1);
if ((ulong)stopReadPosition > _fileSize)
{
stopReadPosition = (long)_fileSize;
}
requestMessage.Headers.Range = new System.Net.Http.Headers.RangeHeaderValue(startReadPosition, stopReadPosition);
requestMessage.Method = HttpMethod.Get;
HttpResponseMessage response = _httpClient.SendAsync(requestMessage).Result;
byte[] readedBytes = response.Content.ReadAsByteArrayAsync().Result;
int readedBytesCount = readedBytes.Length;
_currentPosition += (ulong)readedBytesCount;
logger.Trace($"Bytes readed | {readedBytesCount} | startReadPosition {startReadPosition} | stopReadPosition | {stopReadPosition}");
Span<byte> byteSpan = new Span<byte>(buf.ToPointer(), (int)len);
readedBytes.CopyTo(byteSpan);
return readedBytesCount;
}
catch (Exception ex)
{
logger.Error(ex, "Media reading general exception");
return -1;
}
}
public override bool Seek(ulong offset)
{
try
{
logger.Trace($"Seeking media with offset | {offset}");
_currentPosition = offset;
return true;
}
catch (Exception ex)
{
logger.Error(ex, "MediaInput seekeing general error");
return false;
}
}
This solution seams to work, but there are two unresolved problems:
There is about 8s lag between libvlcsharp starts reading stream and video goes live (waiting time in web browser is about 2s).
Some part of video file at the end is not displayed, because the buffer is too short to hold whole file inside. Related thread
I am new to netty and I am trying to design a solution as below for transfer of file from Server to Client over TCP:
1. Zero copy based file transfer in case of non-ssl based transfer (Using default region of the file)
2. ChunkedFile transfer in case of SSL based transfer.
The Client - Server file transfer works in this way:
1. The client sends the location of the file to be transfered
2. Based on the location (sent by the client) the server transfers the file to the client
The file content could be anything (String /image /pdf etc) and any size.
Now, I get this TooLongFrameException: at the Server side, though the server is just decoding the path received from the client, for running the code mentioned below (Server/Client).
io.netty.handler.codec.TooLongFrameException: Adjusted frame length exceeds 65536: 215542494061 - discarded
at io.netty.handler.codec.LengthFieldBasedFrameDecoder.fail(LengthFieldBasedFrameDecoder.java:522)
at io.netty.handler.codec.LengthFieldBasedFrameDecoder.failIfNecessary(LengthFieldBasedFrameDecoder.java:500)
Now, My question is:
Am I wrong with the order of Encoders and Decoders and its configuration? If so, what is the correct way to configure it to receive a file from the server?
I went through few related StackOverflow posts SO Q1,SO Q2 , SO Q3, SO Q4. I got to know about the LengthFieldBasedDecoder, but I didn't get to know how to configure its corresponding LengthFieldPrepender at the Server (Encoding side). Is it even required at all?
Please point me into the right direction.
FileClient:
public final class FileClient {
static final boolean SSL = System.getProperty("ssl") != null;
static final int PORT = Integer.parseInt(System.getProperty("port", SSL ? "8992" : "8023"));
static final String HOST = System.getProperty("host", "127.0.0.1");
public static void main(String[] args) throws Exception {
// Configure SSL.
final SslContext sslCtx;
if (SSL) {
SelfSignedCertificate ssc = new SelfSignedCertificate();
sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
} else {
sslCtx = null;
}
// Configure the client
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.SO_KEEPALIVE, true)
.handler(new ChannelInitializer<SocketChannel>() {
#Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
if (sslCtx != null) {
pipeline.addLast(sslCtx.newHandler(ch.alloc(), HOST, PORT));
}
pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(64*1024, 0, 8));
pipeline.addLast("frameEncoder", new LengthFieldPrepender(4));
pipeline.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null)));
pipeline.addLast(new ObjectEncoder());
pipeline.addLast( new FileClientHandler()); }
});
// Start the server.
ChannelFuture f = b.connect(HOST,PORT).sync();
// Wait until the server socket is closed.
f.channel().closeFuture().sync();
} finally {
// Shut down all event loops to terminate all threads.
group.shutdownGracefully();
}
}
}
FileClientHandler:
public class FileClientHandler extends ChannelInboundHandlerAdapter{
#Override
public void channelActive(ChannelHandlerContext ctx) {
String filePath = "/Users/Home/Documents/Data.pdf";
ctx.writeAndFlush(Unpooled.wrappedBuffer(filePath.getBytes()));
}
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("File Client Handler Read method...");
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
}
}
FileServer:
/**
* Server that accept the path of a file and echo back its content.
*/
public final class FileServer {
static final boolean SSL = System.getProperty("ssl") != null;
static final int PORT = Integer.parseInt(System.getProperty("port", SSL ? "8992" : "8023"));
public static void main(String[] args) throws Exception {
// Configure SSL.
final SslContext sslCtx;
if (SSL) {
SelfSignedCertificate ssc = new SelfSignedCertificate();
sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
} else {
sslCtx = null;
}
// Configure the server.
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_KEEPALIVE, true).handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
#Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
if (sslCtx != null) {
pipeline.addLast(sslCtx.newHandler(ch.alloc()));
}
pipeline.addLast("frameDecoder",new LengthFieldBasedFrameDecoder(64*1024, 0, 8));
pipeline.addLast("frameEncoder", new LengthFieldPrepender(4));
pipeline.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null)));
pipeline.addLast(new ObjectEncoder());
pipeline.addLast(new ChunkedWriteHandler());
pipeline.addLast(new FileServerHandler());
}
});
// Start the server.
ChannelFuture f = b.bind(PORT).sync();
// Wait until the server socket is closed.
f.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
FileServerHandler:
public class FileServerHandler extends ChannelInboundHandlerAdapter {
#Override
public void channelRead(ChannelHandlerContext ctx, Object obj) throws Exception {
RandomAccessFile raf = null;
long length = -1;
try {
ByteBuf buff = (ByteBuf)obj;
byte[] bytes = new byte[buff.readableBytes()];
buff.readBytes(bytes);
String msg = new String(bytes);
raf = new RandomAccessFile(msg, "r");
length = raf.length();
} catch (Exception e) {
ctx.writeAndFlush("ERR: " + e.getClass().getSimpleName() + ": " + e.getMessage() + '\n');
return;
} finally {
if (length < 0 && raf != null) {
raf.close();
}
}
if (ctx.pipeline().get(SslHandler.class) == null) {
// SSL not enabled - can use zero-copy file transfer.
ctx.writeAndFlush(new DefaultFileRegion(raf.getChannel(), 0, length));
} else {
// SSL enabled - cannot use zero-copy file transfer.
ctx.writeAndFlush(new ChunkedFile(raf));
}
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
System.out.println("Exception server.....");
}
}
I referred Netty In Action and code samples from here
There are multiple things wrong with your server/client. First thing the SSL, for the client you don't need to initialize a SslContext for a server instead you would do something like this:
sslCtx = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();
On the server side of things you use a SelfSignedCertificate which in itself isn't wrong but would like to remind you that it should only be used for debugging purposes and not in production. In addition you use the ChannelOption.SO_KEEPALIVE which isn't recommended since the keepalive interval is OS-dependent. Furthermore you added Object En-/Decoder to your pipeline which in your case don't do anything useful so you can remove them.
Also you configured your LengthFieldBasedFrameDecoder wrong due to an incomplete and wrong parameter list. In the netty docs you need the version of the constructor which defines the lengthFieldLength and initialBytesToStrip. Besides the not stripping the length field you also defined the wrong lengthFieldLength which should be the same as your LengthFieldPrepender's lengthFieldLength which is 4 bytes. In conlusion you could use the constructor like this:
new LengthFieldBasedFrameDecoder(64 * 1024, 0, 4, 0, 4)
In both your handler you don't specify a Charset when en-/decoding your String which could lead to problems because if no ´Charset´ is defined the systems default will be used which could vary. You could do something like this:
//to encode the String
string.getBytes(StandardCharsets.UTF_8);
//to decode the String
new String(bytes, StandardCharsets.UTF_8);
Additionally you tried to use the DefaultFileRegion if no SslHandler was added to the pipeline which would have been fine if you didn't added the LengthFieldHandler since they would need a memory copy of the byte[] to send to added the length field. Moreover I would recommend using the ChunkedNioFile instead of the ChunkedFile because it's nonblocking which is always a good thing. You would do this like that:
new ChunkedNioFile(randomAccessFile.getChannel())
One final thing on how to decode a ChunkedFile as it's split in chunks you can simply assamble them tougether with a simple OutputStream. Here's an old file handler of mine:
public class FileTransferHandler extends SimpleChannelInboundHandler<ByteBuf> {
private final Path path;
private final int size;
private final int hash;
private OutputStream outputStream;
private int writtenBytes = 0;
private byte[] buffer = new byte[0];
protected FileTransferHandler(Path path, int size, int hash) {
this.path = path;
this.size = size;
this.hash = hash;
}
#Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception {
if(this.outputStream == null) {
Files.createDirectories(this.path.getParent());
if(Files.exists(this.path))
Files.delete(this.path);
this.outputStream = Files.newOutputStream(this.path, StandardOpenOption.CREATE, StandardOpenOption.APPEND);
}
int size = byteBuf.readableBytes();
if(size > this.buffer.length)
this.buffer = new byte[size];
byteBuf.readBytes(this.buffer, 0, size);
this.outputStream.write(this.buffer, 0, size);
this.writtenBytes += size;
if(this.writtenBytes == this.size && MurMur3.hash(this.path) != this.hash) {
System.err.println("Received file has wrong hash");
return;
}
}
#Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
if(this.outputStream != null)
this.outputStream.close();
}
}
How can I retrieve and display images from a database in a JSP page?
Let's see in steps what should happen:
JSP is basically a view technology which is supposed to generate HTML output.
To display an image in HTML, you need the HTML <img> element.
To let it locate an image, you need to specify its src attribute.
The src attribute needs to point to a valid http:// URL and thus not a local disk file system path file:// as that would never work when the server and client run at physically different machines.
The image URL needs to have the image identifier in either the request path (e.g. http://example.com/context/images/foo.png) or as request parameter (e.g. http://example.com/context/images?id=1).
In JSP/Servlet world, you can let a Servlet listen on a certain URL pattern like /images/*, so that you can just execute some Java code on specific URL's.
Images are binary data and are to be obtained as either a byte[] or InputStream from the DB, the JDBC API offers the ResultSet#getBytes() and ResultSet#getBinaryStream() for this, and JPA API offers #Lob for this.
In the Servlet you can just write this byte[] or InputStream to the OutputStream of the response the usual Java IO way.
The client side needs to be instructed that the data should be handled as an image, thus at least the Content-Type response header needs to be set as well. You can obtain the right one via ServletContext#getMimeType() based on image file extension which you can extend and/or override via <mime-mapping> in web.xml.
That should be it. It almost writes code itself. Let's start with HTML (in JSP):
<img src="${pageContext.request.contextPath}/images/foo.png">
<img src="${pageContext.request.contextPath}/images/bar.png">
<img src="${pageContext.request.contextPath}/images/baz.png">
You can if necessary also dynamically set src with EL while iterating using JSTL:
<c:forEach items="${imagenames}" var="imagename">
<img src="${pageContext.request.contextPath}/images/${imagename}">
</c:forEach>
Then define/create a servlet which listens on GET requests on URL pattern of /images/*, the below example uses plain vanilla JDBC for the job:
#WebServlet("/images/*")
public class ImageServlet extends HttpServlet {
// content=blob, name=varchar(255) UNIQUE.
private static final String SQL_FIND = "SELECT content FROM Image WHERE name = ?";
#Resource(name="jdbc/yourDB") // For Tomcat, define as <Resource> in context.xml and declare as <resource-ref> in web.xml.
private DataSource dataSource;
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String imageName = request.getPathInfo().substring(1); // Returns "foo.png".
try (Connection connection = dataSource.getConnection(); PreparedStatement statement = connection.prepareStatement(SQL_FIND)) {
statement.setString(1, imageName);
try (ResultSet resultSet = statement.executeQuery()) {
if (resultSet.next()) {
byte[] content = resultSet.getBytes("content");
response.setContentType(getServletContext().getMimeType(imageName));
response.setContentLength(content.length);
response.getOutputStream().write(content);
} else {
response.sendError(HttpServletResponse.SC_NOT_FOUND); // 404.
}
}
} catch (SQLException e) {
throw new ServletException("Something failed at SQL/DB level.", e);
}
}
}
That's it. In case you worry about HEAD and caching headers and properly responding on those requests, use this abstract template for static resource servlet.
See also:
How should I connect to JDBC database / datasource in a servlet based application?
How to upload an image and save it in database?
Simplest way to serve static data from outside the application server in a Java web application
I suggest you address that as two problems. There are several questions and answer related to both.
How to load blob from MySQL
See for instance Retrieve image stored as blob
How to display image dynamically
See for instance Show thumbnail dynamically
I've written and configured the code in JSP using Oracle database.
Hope it will help.
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.http.HttpSession;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class displayfetchimage
*/
#WebServlet("/displayfetchimage")
public class displayfetchimage extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* #see HttpServlet#HttpServlet()
*/
public displayfetchimage() {
super();
// TODO Auto-generated constructor stub
}
/**
* #see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
Statement stmt = null;
String sql = null;
BufferedInputStream bin = null;
BufferedOutputStream bout = null;
InputStream in = null;
response.setContentType("image/jpeg");
ServletOutputStream out;
out = response.getOutputStream();
Connection conn = employee.DbConnection.getDatabaseConnection();
HttpSession session = (HttpSession) request.getSession();
String ID = session.getAttribute("userId").toString().toLowerCase();
try {
stmt = conn.createStatement();
sql = "select user_image from employee_data WHERE username='" + ID + "' and rownum<=1";
ResultSet result = stmt.executeQuery(sql);
if (result.next()) {
in = result.getBinaryStream(1);// Since my data was in first column of table.
}
bin = new BufferedInputStream(in);
bout = new BufferedOutputStream(out);
int ch = 0;
while ((ch = bin.read()) != -1) {
bout.write(ch);
}
} catch (SQLException ex) {
Logger.getLogger(displayfetchimage.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
if (bin != null)
bin.close();
if (in != null)
in.close();
if (bout != null)
bout.close();
if (out != null)
out.close();
if (conn != null)
conn.close();
} catch (IOException | SQLException ex) {
System.out.println("Error : " + ex.getMessage());
}
}
}
// response.getWriter().append("Served at: ").append(request.getContextPath());
/**
* #see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Statement stmt = null;
String sql = null;
BufferedInputStream bin = null;
BufferedOutputStream bout = null;
InputStream in = null;
response.setContentType("image/jpeg");
ServletOutputStream out;
out = response.getOutputStream();
Connection conn = employee.DbConnection.getDatabaseConnection();
HttpSession session = (HttpSession) request.getSession();
String ID = session.getAttribute("userId").toString().toLowerCase();
try {
stmt = conn.createStatement();
sql = "select user_image from employee_data WHERE username='" + ID + "' and rownum<=1";
ResultSet result = stmt.executeQuery(sql);
if (result.next()) {
in = result.getBinaryStream(1);
}
bin = new BufferedInputStream(in);
bout = new BufferedOutputStream(out);
int ch = 0;
while ((ch = bin.read()) != -1) {
bout.write(ch);
}
} catch (SQLException ex) {
Logger.getLogger(displayfetchimage.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
if (bin != null)
bin.close();
if (in != null)
in.close();
if (bout != null)
bout.close();
if (out != null)
out.close();
if (conn != null)
conn.close();
} catch (IOException | SQLException ex) {
System.out.println("Error : " + ex.getMessage());
}
}
}
}
Try to flush and close the output stream if it does not display.
Blob image = rs.getBlob(ImageColName);
InputStream in = image.getBinaryStream();
// Output the blob to the HttpServletResponse
response.setContentType("image/jpeg");
BufferedOutputStream o = new BufferedOutputStream(response.getOutputStream());
byte by[] = new byte[32768];
int index = in.read(by, 0, 32768);
while (index != -1) {
o.write(by, 0, index);
index = in.read(by, 0, 32768);
}
o.flush();
o.close();
I used SQL SERVER database and so the answer's code is in accordance. All you have to do is include an <img> tag in your jsp page and call a servlet from its src attribute like this
<img width="200" height="180" src="DisplayImage?ID=1">
Here 1 is unique id of image in database and ID is a variable. We receive value of this variable in servlet. In servlet code we take the binary stream input from correct column in table. That is your image is stored in which column. In my code I used third column because my images are stored as binary data in third column. After retrieving input stream data from table we read its content in an output stream so it can be written on screen. Here is it
import java.io.*;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.*;
import javax.servlet.http.*;
import model.ConnectionManager;
public class DisplayImage extends HttpServlet {
public void doGet(HttpServletRequest request,HttpServletResponse response)
throws IOException
{
Statement stmt=null;
String sql=null;
BufferedInputStream bin=null;
BufferedOutputStream bout=null;
InputStream in =null;
response.setContentType("image/jpeg");
ServletOutputStream out;
out = response.getOutputStream();
Connection conn = ConnectionManager.getConnection();
int ID = Integer.parseInt(request.getParameter("ID"));
try {
stmt = conn.createStatement();
sql = "SELECT * FROM IMAGETABLE WHERE ID="+ID+"";
ResultSet result = stmt.executeQuery(sql);
if(result.next()){
in=result.getBinaryStream(3);//Since my data was in third column of table.
}
bin = new BufferedInputStream(in);
bout = new BufferedOutputStream(out);
int ch=0;
while((ch=bin.read())!=-1)
{
bout.write(ch);
}
} catch (SQLException ex) {
Logger.getLogger(DisplayImage.class.getName()).log(Level.SEVERE, null, ex);
}finally{
try{
if(bin!=null)bin.close();
if(in!=null)in.close();
if(bout!=null)bout.close();
if(out!=null)out.close();
if(conn!=null)conn.close();
}catch(IOException | SQLException ex){
System.out.println("Error : "+ex.getMessage());
}
}
}
}
After the execution of your jsp or html file you will see the image on screen.
You can also create custom tag for displaying image.
1) create custom tag java class and tld file.
2) write logic to display image like conversion of byte[] to string by Base64.
so it is used for every image whether you are displaying only one image or multiple images in single jsp page.
I am reading a excel file now I want to pass it to the jsp file.
I need to pass the test object to the jsp file so that I can display it to the browser.
import java.io.IOException;
import java.io.*;
import jxl.Cell;
import jxl.Sheet;
import jxl.Workbook;
import jxl.read.biff.BiffException;
public class Readexcl
{
private String inputFile;
public void setInputFile(String inputFile) {
this.inputFile = inputFile;
}
public void read() throws IOException {
File inputWorkbook = new File(inputFile);
Workbook w;
try {
w = Workbook.getWorkbook(inputWorkbook);
Sheet sheet = w.getSheet(0);
for (int i = 0; i < sheet.getRows(); i++)
{
Cell cell = sheet.getCell(0, i);
System.out.print(cell.getContents()+" ");
cell = sheet.getCell(1, i);
System.out.println(cell.getContents()+" ");
//cell = sheet.getCell(2, i);
//System.out.println(cell.getContents());
}
}
catch (BiffException e)
{
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
Readexcl test = new Readexcl();
test.setInputFile("c:\\DATA.xls");
test.read();
}
}
If you are in a servlet (but the same is valid if you are in a jsp) you don't need to pass anything...just set the apptopriate content type in response, get the ServletOutputStream, read the xls and write using that stream
If you are in a main it unusual but not impossible...just create a servlet that accept base64 encoded data. In your main read the file, encode the stream in base64band then make an http post call to that servlet, posting the encoded data. In your servlet doPost() method decode the data and write out using ServletOutputStream
Under here you find a simple server/client app that sends the integer 5 from the client to the server, which reads it and sends it back to the client. On the server I have placed a latency meter around the DataInputStream.readInt() method, which reads that this method is causing a 400 ms latency.
Server code:
import java.io.*;
import java.net.*;
public class Server {
public static void main(String args[]) {
// declaration section:
ServerSocket echoServer = null;
Socket clientSocket = null;
int x;
// Try to open a server socket on port 4444
try {
echoServer = new ServerSocket(4444);
}
catch (IOException e) {
System.out.println(e);
}
//accept connection
try {
clientSocket = echoServer.accept();
//input
DataInputStream input = new DataInputStream(clientSocket.getInputStream());
//output
DataOutputStream output = new DataOutputStream(clientSocket.getOutputStream());
//loop
while(true){
long time = System.currentTimeMillis();
output.writeInt(input.readInt());
long dtime = System.currentTimeMillis();
System.out.println(dtime-time);
}
//close
// output.close();
// input.close();
// clientSocket.close();
// echoServer.close();
}catch (IOException e) {
System.out.println(e);
}
}
}
Client code:
import java.io.*;
import java.net.*;
public class Client {
public static void main(String[] args) {
// declaration section:
// clientClient: our client socket
// out: output stream
// in: input stream
Socket clientSocket = null;
// Initialization section:
// Try to open a socket on port 4444
// Try to open input and output streams
try{
clientSocket = new Socket("YOUR-IP-HERE", 4444);
//output
DataOutputStream output = new DataOutputStream(clientSocket.getOutputStream());
//input
DataInputStream input = new DataInputStream(clientSocket.getInputStream());
//loop
while(true) {
output.writeInt(5);
//System.out.println(input.readInt());
}
//close
// output.close();
// input.close();
// clientSocket.close();
}catch(Exception e) {
System.out.println("error");
}
}
}
Problem area:
//latencymeter
long time = System.currentTimeMillis();
//problem code
output.writeInt(input.readInt());
//latencymeter
long dtime = System.currentTimeMillis();
System.out.println(dtime-time);
Am I making a mistake in my code or am I not coding efficiently, please let me know.
Thanks
Solved: Adding clientSocket.setTcpNoDelay(true); on both server and client has brought the latency down to a normal 10-20 ms.