2

I am using the following snippet to select and read images from file:

<div class="col-md-6 form-group">
<input type='file' name="image" onchange="readURL(this);"
                    class="form-control" /> 
<img id="userimg" src="#" alt="" />
</div>

.js:

function readURL(input) {
    if (input.files && input.files[0]) {
        var reader = new FileReader();

        reader.onload = function (e) {
            $('#userimg')
                .attr('src', e.target.result)
                .width(150)
                .height(200);
        };

        reader.readAsDataURL(input.files[0]);
    }
}

In my servlet I convert the file to a byte[]:

(request.getParameter("image")).getBytes()

and insert it into a database.

Then, I try to read it from the database and display in a .jsp page, like this:

.jsp

  <div class="panel panel-success">
                        <h2>Pictures</h2>
                        <%
                            List<byte[]> pics = PictureTable.getConcertPictures(concertBean.getId());
                        %>
                        <%
                            for (byte[] p : pics) {
                                String encoded = ImageHelper.encodeImage(p);
                        %>
                        <img src="data:image/jpg;base64,<%=encoded%>">
                        <%
                            }
                        %>
    </div>

ImageHelper.java:

public static String encodeImage(byte[] img) {
        return Base64.encode(img);
    }

but the image is not displayed correctly (something does get inserted into the database).

Edit:

I've modified my code:

.jsp:

<div class="comments">
                <div class="panel panel-success">
                    <h2>Pictures</h2>
                    <%
                        List<byte[]> pics = PictureTable.getConcertPictures(concertBean.getId());
                    %>
                    <%
                        for (byte[] p : pics) {
                            String encoded = ImageHelper.encodeImage(p);
                    %>
                    <img src="data:image/jpg;base64,<%=encoded%>">
                    <%
                        }
                    %>
                </div>
                <!-- /container -->
                <form
                    action="GalleryController?action=add_concert_picture&concertID=<%=concertBean.getId()%>"
                    method="post" class="panel panel-success"
                    enctype="multipart/form-data">
                    <div class="col-md-6 form-group">
                        <input type='file' name="image" class="form-control" />
                    </div>
                    <button type="submit" class="btn">Submit</button>
                </form>
            </div>

servlet:

Part filePart = request.getPart("image");
InputStream fileContent = filePart.getInputStream();
PictureTable.insertConcertPicture(cid, user.getUser().getId(), fileContent);

PictureTable.java:

public static void insertConcertPicture(int concertId, int userId, InputStream photo) {
        try (Connection connection = ConnectionPool.getConnectionPool().checkOut()) {

            PreparedStatement ps = connection
                    .prepareStatement("INSERT INTO concertphototable(ConcertId, UserId, Photo) VALUES(?,?,?)");

            ...
            ps.setBinaryStream(3, photo);

            ps.executeUpdate();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

The image is still not shown correctly, but now the "submit" part of the page is not even visible.

Eutherpy
  • 4,471
  • 7
  • 40
  • 64
  • 2
    Why not write bytes directly to response without Base64 encoding and point `img` tag url directly to that servlet? – tsolakp Dec 21 '17 at 19:57
  • What do you get when you use `Base64.getEncoder().encode(img)` ? Also did you check that your images are in JPEG? – pyb Dec 23 '17 at 21:33
  • 1
    @pyb Definitely JPG. I checked - what's being encoded is the name of the picture. I just decoded what `encode(img)` returned, and got the name of my file. – Eutherpy Dec 23 '17 at 21:44
  • You said the submit button isn't visible anymore. What do you get when you do View Source in your browser? – pyb Dec 24 '17 at 01:47
  • @pyb I get: `src="data:image/jpg;base64,/9j/4AAQSkZJRgABAQEAAAAAAAD/4Zf2RXhpZgAATU0AKgAAAAg...this goes on for veeeeeeery long`. After that, there is the code for the submit button. – Eutherpy Dec 24 '17 at 08:25

2 Answers2

3

There are many ways to do this. I would go by returning base64 String and store it in the database. Then you can return it to the JSP to render it.

To do this you have to:

<input type="file" name="file" onchange="encodeImageFileAsURL(this)" />
<form method="POST" action="/upload">
    <input id="image" type="text" name="image" value="asdf" /><br />
    <br /> <input type="submit" value="Submit" />
</form>
<script type="text/javascript">
    function encodeImageFileAsURL(element) {
        var file = element.files[0];
        var reader = new FileReader();
        reader.onloadend = function() {
            document.getElementById("image").value = reader.result;
        }
        reader.readAsDataURL(file);
    }
</script>

In your /upload controller:

@PostMapping("/upload")
public ModelAndView singleFileUpload(@RequestParam("image") String image, RedirectAttributes redir) {
    jdbcTemplate.update("INSERT INTO concertphototable(ConcertId, UserId, Photo) VALUES(?, ?, ?)", 1, 1, image);
    String imageString = jdbcTemplate.queryForObject("Select Photo from concertphototable where ConcertId = " + 1,
            String.class);
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.setViewName("redirect:uploadStatus");
    redir.addFlashAttribute("image", imageString);
    return modelAndView;
}

And then to the uploadStatus page:

<img src=${image}>

Don't get confused of the Spring part you should have in mind that you have to post the base64 String from jsp to server then store it. Then you retrieve it as String and pass it to the jsp in the image src tag and the image will be displayed. If there is no specific reason for you to store the image as blob to the database you have to go with the way above.

ddarellis
  • 3,912
  • 3
  • 25
  • 53
1

I believe some of your issues are technical and some are methodology related. My response primarily focuses on the methodology.

We did something similar with a JavaScript client and C# Web API server. We adopted a very simple development/verification model (below) that allowed us to quickly find and isolate errors at each point where the data undergoes a conversion.

We initially struggled with many of the same issues you seem to be experiencing. How we solved our issues may be of some assistance to you.

User tsolakp made a good point. You should consider returning the byte data directly. If you are going to go binary from the client to server then I would recommend you seriously consider it for the download data as well. We use binary uploads/downloads for most of our data. I know this is not in vogue nowadays, but it sure is a lot more efficient in our system.

You mentioned that:

src="data:image/jpg;base64,/9j/4AAQSkZJRgABAQEAAAAAAAD/4Zf2RXhpZgAATU0AKgAAAAg...this goes on for veeeeeeery long.

That is something you can control. I would recommend that you create a very small jpeg that is easily read/verified by a human and use it to incrementally verify your upload and download flows.

We used the following code flow on the javascript client upload of the binary data.

var byte[] = atob(jpeg string);
var xhr = new XMLHttpRequest();
xhr.open...
xhr.onload...
xhr.setRequestHeader("Content-type", "application/octet-stream");
xhr.send(new Uint8Array(byte[]));

We used a very small jpeg (e.g., 5x10px) so that it was easy verify at each step of the data flow.

  1. Convert jpeg string to byte[], then convert it back to a string and verify it to make sure everything is working correctly.

  2. As illustrated in the above Http code, we had to covert the JavaScript byte[] to an Uint8Array(byte[]) in order to transmit it to the server. Before we sent it to the server, we converted it back to the byte[], and then to the jpeg string format to verify the overall flow to this point. We found a couple of issues that were easy to identify by breaking the flow into these small pieces.

  3. Once we got this far, we added the client-server communication. On arrival to the server, we verified that the binary data on the server matched our client data extracted in step 2.

  4. Before storing the data on the server, we returned the uploaded data to the client to once again verify that it was the same at each point and could be properly decoded. Here we found a couple of issues and identified the need for a media converter (your Java issue will likely be different).

  5. Lastly, once the data was verified to this point, we then stepped through the system data storing process in like manner.

We had 6-7 issues that were revealed by taking small progressive steps to push the data to server and to retrieve it. Initially we did the whole end-to-end thing and hit a break wall. That is why we switched to a piece meal development/validation process. My 2 cents worth.

If you break the problem down it will probably help to address the issue that you mentioned here:

but the image is not displayed correctly (something does get inserted into the database).

The something is very important all along the data path.

I know the download code on your system will be different, but I have included ours to illustrate that there are a number of sequential steps that can introduce errors that are easily masked if you do not break the problem down into smaller pieces.

var xhr = new XMLHttpRequest();
xhr.open...
  xhr.responseType = "arraybuffer";
xhr.onload... = () => {
    if (xhr.status === 200) {
      var arrayBuffer = xhr.response;
      var unsigned8Int = new Uint8Array(arrayBuffer);
      // Here we had to convert the unsigned8bit array back to a JavaScript array.
      // Then convert js byte[] to a string and prepend the image header as
      // you are doing in your code.
      //
      // The last step was to prepend the image header as you are doing, but
      // we used binary to asci. 
      var data = "data:image/jpeg;base64," + btoa(byte[]);

    };
    xhr.setRequestHeader("Content-type", "application/octet-stream");
    xhr.send();

I haven’t used Java in a number of years, but I will see if I can dredge up some more technical info that might be relevant to your actual design.

Highdown
  • 646
  • 1
  • 11
  • 23