I am new to Java development so apologies in advance if I am asking something stupid.
I am trying to retrieve an image and it's thumbnail from sql database.
I get data from ResultSet in BinaryStream format and then convert it to byte[].
For thumbnail it works fine and for original image too I am able to retrieve BinaryStream using getBinaryStream method But when I convert it to byte[], the array remain empty for some reason.
binaryStream = rs.getBinaryStream("image");
thumbBinaryStream = rs.getBinaryStream("thumbnail");
if (binaryStream != null) {
// Tested on following line and I get empty imageBytes
byte[] imageBytes = IOUtils.toByteArray(binaryStream);
thisRecord.put("image", DatatypeConverter.printBase64Binary(imageBytes)); // imageBytes is empty here
}
We probably need more information, especially about the datatypes of the columns, but maybe it helps to retrieve the stream from the BLOB like in this example:
if (rs.getMetaData().getColumnType(column) == Types.BLOB) {
in = rs.getBlob(column).getBinaryStream();
} else {
in = rs.getBinaryStream(column);
}
To be sure:
Statement and ResultSet must be closed and getBinaryStream only used when the ResultSet is still open, like:
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
InputStream binaryStream = rs.getBinaryStream("image");
InputStream thumbBinaryStream = rs.getBinaryStream("thumbnail");
if (binaryStream != null) {
// Tested on following line and I get empty imageBytes
byte[] imageBytes = IOUtils.toByteArray(binaryStream);
thisRecord.put("image", DatatypeConverter.printBase64Binary(imageBytes));
boolean mustGenerateThumbnail = thumbBinaryStream == null;
if (mustGenerateThumbnail ) {
thumbBinaryStream = generateThumbnail(imageBytes);
}
byte[] thumbBytes = IOUtils.toByteArray(thumbBinaryStream);
thisRecord.put("thumbnail", DatatypeConverter.printBase64Binary(thumbBytes));
Here we are at the error. At this point thumbBinaryStream is read till the end, so do:
if (mustGenerateThumbnail ) {
ByteArrayInputStream baIn = new ByteArrayInputStream(thumbBytes);
saveThumbnailForRecordWithId(baIn, floor_drawing_id);
}
}
}
}
(Here I used try-with-resources to automatically close the ResultSet even on thrown exception.)
Furthermore there is a more general class for Base64. Should you in future have the need for such.
DatatypeConverter.printBase64Binary(thumbBytes)
Base64.getEncoder().encodeToString(thumbBytes)
Related
I'm trying to upload a blob directly from a stream, since I don't know the length of the stream I decided to try with this answer.
This doesn't work, even though it reads from the stream and doesn't throw any exceptions the content isn't uploaded to my container.
I have no problem uploading from files, it only occurs when uploading from a stream.
This is my code, I added a few outs to check whether it was reading something or not but that wasn't the problem:
try {
CloudBlockBlob blob = PublicContainer.getBlockBlobReference(externalFileName);
if (externalFileName.endsWith(".tmp")) {
blob.getProperties().setContentType("image/jpeg");
}
BlobOutputStream blobOutputStream = blob.openOutputStream();
int next = input.read();
while (next != -1) {
System.err.println("writes");
blobOutputStream.write(next);
next = input.read();
}
blobOutputStream.close();
return blob.getUri().toString();
} catch (Exception usex) {
System.err.println("ERROR " + usex.getMessage());
return "";
}
It doesn't fails but it doesn't works.
Is there another way of doing this? Or am I missing something?
UPDATE: I've been checking and I think that the problem is with the InputStream itself, but I don't know why since the same stream will work just fine if I use it to upload to Amazon s3 for instance
I tried to reproduce your issue, but failed. According to your code, it seems that the only obvious missing thing is no calling blobOutputStream.flush(); before close the output stream via blobOutputStream.close();, but it works if missing flush method
Here is my testing code as below.
String STORAGE_CONNECTION_STRING_TEMPLATE = "DefaultEndpointsProtocol=https;AccountName=%s;AccountKey=%s;";
String accountName = "xxxx";
String key = "XXXXXX";
CloudStorageAccount account = CloudStorageAccount.parse(String.format(STORAGE_CONNECTION_STRING_TEMPLATE, accountName, key));
CloudBlobClient client = account.createCloudBlobClient();
CloudBlobContainer container = client.getContainerReference("mycontainer");
container.createIfNotExists();
String externalFileName = "test.tmp";
CloudBlockBlob blob = container.getBlockBlobReference(externalFileName);
if (externalFileName.endsWith(".tmp")) {
blob.getProperties().setContentType("image/jpeg");
}
BlobOutputStream blobOutputStream = blob.openOutputStream();
String fileName = "test.jpg";
InputStream input = new FileInputStream(fileName);
int next = -1;
while((next = input.read()) != -1) {
blobOutputStream.write(next);
}
blobOutputStream.close(); // missing in your code, but works if missing.
input.close();
If you can update in more details, I think it's help for analysising the issue. Any concern, please feel free to let me know.
I use wildfly-8.2.0.Final. There is connection pool (Oracle) on this server.
Look at following code:
public ArrayList<HashMap<String, Object>> fetchSome(String query)
throws OracleQueryProcessorException {
ArrayList<HashMap<String, Object>> result = new ArrayList<HashMap<String, Object>>();
try {
Context initCtx = new InitialContext();
DataSource ds = (DataSource) initCtx.lookup(driver);
try (Connection con = ds.getConnection();
PreparedStatement stmt = con.prepareStatement(query)) {
try (ResultSet rs = stmt.executeQuery()) {
ResultSetMetaData rsmd = rs.getMetaData();
rs.next();
HashMap<String, Object> row = new HashMap<String, Object>();
String name = rsmd.getColumnName(1);
Object value = rs.getObject(1);
if (value instanceof Blob) {
Blob bl = (Blob) value;
if (bl.length() > 0)
value = bl.getBinaryStream();
else
value = null;
}
row.put(name, value);
result.add(row);
}
} catch (SQLException e) {
throw new OracleQueryProcessorException();
}
} catch (NamingException e) {
throw new OracleQueryProcessorException();
}
return result;
}
And this is usage of this function:
InputStream is = (InputStream) fetchSome("SELECT BLOB_FIELD FROM TEST WHERE ID = 1").get(0).get("BLOB_FIELD");
if (is != null) {
byte[] a = new byte[3];
is.read(a);
}
Reading from this stream is working!! How can it work? Connection is closed (cause using try-with-resources clause). Reading from this stream take no connection from pool (All pool's connections are available).
fetchSome() opens a Connection, sends the query, and then reads the data back into the resulting ArrayList. Then fetchSome closes the Connection and returns the ArrayList. The code you are curious about then reads from the ArrayList that was returned, not from the Connection that was, as you correctly noticed, closed.
By the time your method returns, all database communication has finished, and all the data has been copied into the returned list, from which it can then be read as often and as late as you want, without needing a Connection again.
Does it really work for various BLOB sizes? Good thresholds are:
4000B (limit where BLOB might be in lined in the row - not stored aside)
2000B (maximum size for RAW) - BLOB can be casted to RAW somewhere
16KB, 32KB
some huge value bigger than JVM heap size
AFAIK on OCI level(C client library) LOBs might be "pre-allocated" .i.e. some smaller portion of BLOB can be sent to client, although it was not requested yet by the client. This should reduce number of round-trips between database and client.
Also you should try check v$instance view to check whether the connection really was closed. Cooperation between JDBC and Oracle is tricky sometimes.
For example temporary LOBs created via Connection.createBLOB() are treaded differently than any other temporary lobs by the database. I think it is because Oracle database can not talk to JVM GC and it does not know when really the java instance was disposed. So these lobs are kept in the database "forever".
I have the following structure. I am joining some tables together to get a result from my SQL database. I have a column that actually stores an BLOB image in it. I would like to know how can I retrieve this information in a variable that can then be used to load this image in a Graphical User Interface such as swing window.
Here is my code on how I am reading other - String - fields of the database.
ResultSet result = statement.executeQuery(SQLQuery);
ResultSetMetaData metaData = rs.getMetaData();
while (result.next())
{
for (int i = 1; i <= columnsNumber; i++)
{
String AttributeName = metaData.getColumnName(i);
String AttributeValue = result.getString(i);
if(AttributeName == "UserName")
{
String userName = AttributeValue;
}
if(AttributeName == "UserImage")
{
//code here to get the BLOB user Image.?
}
}
}
And lastly an idea of how can, with a given picture (from the filesystem) to store this image as BOLB in the database?
Cheers.
I read about this one but I think this implementation can't help me.
There you go, just integrate both your and my loop:
while (resultSet.next()) {
// String name = resultSet.getString(1);
// String description = resultSet.getString(2);
File image = new File("your path");
FileOutputStream fos = new FileOutputStream(image);
byte[] buffer = new byte[1];
InputStream is = resultSet.getBinaryStream(3);
while (is.read(buffer) > 0) {
fos.write(buffer);
}
fos.close();
}
Of course remember to properly modify path and InputStream is = resultSet.getBinaryStream(3); lines. Good luck!
I am trying to display images in a Jsp page using Jstl, and image paths are passed on by a servlet. The Jsp page actually displays only one of the retrieved images and throws a NullPointerException.
The Jsp looks like this:
<c:forEach items="${images}" var="img">
<div class="col-md-3">
<img src="displayImg?imageId=${img.imageId}">
</div>
</c:forEach>
And servlet doGet method looks like this:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int imageId = Integer.parseInt(request.getParameter("imageId"));
Image img = imageDao.getImageById(imageId);
response.setContentType("image/" + img.getImageType());
File f = new File(img.getImagePath());
BufferedImage bi = ImageIO.read(f);
OutputStream out = response.getOutputStream();
ImageIO.write(bi, img.getImageType(), out);
out.close();
}
I can't understand why this servlet manages to serve one image and fails on others with a NullPointerException when the request parameter is correct. I have an impression like it is a concurrency issue because the Jsp displays arbitrary image.
Any help please?
Here is the DAO:
public Image getImageById(int imageId) {
String query = "SELECT * FROM images WHERE imageId=?";
Image img = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
connection = ConnectionManager.getConnection();
ps = connection.prepareStatement(query);
ps.setInt(1, imageId);
rs = ps.executeQuery();
if (rs.next()) {
img = new Image();
img.setImageId(rs.getInt("imageId")); //NPE thrown here
img.setImagePath(rs.getString("imagePath"));
img.setImageType(rs.getString("imageType"));
...
img.setDescription(rs.getString("description"));
img.setCreatedOn(rs.getTimestamp("createdOn"));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(rs);
close(ps);
close(connection); //Removed this and problem disappears
}
return img;
}
Solved!!
It is weird but I just avoided closing the connection to the database and all my images are displaying correctly with no errors. I guess closing the connection after every single DB access is problematic as it could not close at the exact time you want it to but maybe in the middle of another call to the DB. Now I wonder, not closing the connection at all is problematic too. What to do in that case?
Weird as imageId is used as column name in the query, and there is a record, rs.next().
Maybe it is not an int but long, or SQL VARCHAR.
Maybe MySQL was so overly clever to leave out imageId as this was specified in the WHERE?
Or somehow in this case you need to use rs.getInt("images.imageId").
img.setImageId(rs.getInt("imageId")); //NPE thrown here
could however be written (circumvented) as
img.setImageId(mageId);
I would check that there is no devious typo: í (accent), Cyrillic e (if you are East European) or tab char. Honestly said your code looks neat, but similar errors happen with much less neat code: ps, ps2, rs, rs2 as fields, playing havoc with concurrent uses and apt to typos.
BTW writing could be done faster with less memory resources:
Path path = Paths.get(img.getImagePath());
OutputStream out = response.getOutputStream();
Files.copy(path, out);
On the search for errors:
Investigating the error seems the only option:
ResultSetMetaData meta = rs.getMetaData();
int numberOfColumns = meta.getColumnCount();
for (int i = 1; i <= numberOfColumns; ++i) {
log(... i, meta.getColumnName(i), meta.getColumnLabel(i) ...);
}
Just read the stack trace:
at com.mysql.jdbc.ResultSetImpl.buildIndexMapping(ResultSetImpl.java:674)
at com.mysql.jdbc.ResultSetImpl.findColumn(ResultSetImpl.java:1029)
at com.mysql.jdbc.ResultSetImpl.getInt(ResultSetImpl.java:2566)
at be.kayiranga.daoImpl.ImageDaoImpl.getImageById(ImageDaoImpl.java:114)
So, the exception is thrown by
rs.getInt("imageId")
which means that there is no column named "imageId" in the result set.
You should never use select *. Use explicit column names, and use these explicit column names when getting data from the result set:
select imageId, imagePath, imageType, description, createdOn from ...
THis answer might not help you completely, as no one I believe understands what you are doing exactly. I am presuming you have a JSP page and a form inside, I can show you how to display the image as a string.
public showImage(){
BASE64Encoder base64Encoder = new BASE64Encoder();
// your method to retreive the image
if(image == null){
File imagePath = new File("/home/akshay/images.jpeg"); // display alternate image
try {
BufferedImage bufferedImage = ImageIO.read(imagePath);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, "png", byteArrayOutputStream);
//Below person is the main object model, and String is the image in string
person.setProfilePhotoString("data:image/png;base64," + base64Encoder.encode(byteArrayOutputStream.toByteArray()));
} catch (IOException e) {
e.printStackTrace();
}
} else{
person1.setProfilePhotoString("data:image/png;base64," + base64Encoder.encode(person1.getProfilephoto()));
}
}
JSP page :
<form:form action="${showAction}" commandName="person">
<table>
<tr>
<td><img src= "${person.profilePhotoString}" height=100 width=100/>
</tr>
</table>
</form>
I am getting exception while fatch image from database and display image on screen. Please help me to solve this error..
int questionid ;
Connection conn = null;
conn= new DBFunction().getconnect();
Statement stmt = conn.createStatement();
questionid=4;
try
{
conn.setAutoCommit (false);
// get the image from the database
Blob img ;
byte[] imgData = null ;
String req = "Select img From questionlist Where queid = " + questionid ;
ResultSet rset = stmt.executeQuery ( req );
while (rset.next ())
{
img = rset.getBlob("img");
imgData = img.getBytes(1,(int)img.length());
}
// display the image
response.reset();
response.setContentType("image/gif");
OutputStream o = response.getOutputStream();
o.write(imgData);
o.flush();
o.close();
}
catch (Exception e)
{
e.printStackTrace();
}
Let me know where am i wrong ?
It is not the better practice to write the java codes inside jsp as jsp calls printWriter by default. Scriptlets are not advised over decades . try moving the code into a java file , preferably a servlet.
Also it is not better option to store images into Database , try to use the diskspace instead. As far as your question try out.clear()
see How to avoid Java code in JSP files?