简体   繁体   中英

Amazon S3 multiple file uploads using Parts

After many hours spent on reading documentations and searching for some tips on the internet, I finally decided to create a Stack OverFlow account; seem desperate? Haha, let's go.

EDIT (05/16/2012) : As I found the solution (by myself, finally), I replaced my old code with my running code.

I go to /admin/lstimg (ListImage), which gets an image (URL) records collection, from my database. Then the collection is set as an attribute which allows me to use the data in my jsp file.

Note : The public class ListImageServlet extends HttpServlet and is annotated with MultipartConfig .

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException
    {
        try
        {
            Collection images = ImageFacade.getIjc().findImageEntities();
            request.setAttribute("images", images);
            request.getRequestDispatcher("/adm/img/lstimg.jsp").forward(request, response);
        }

        catch (Exception ex)
        {
            Logger.getLogger(ListImageServlet.class.getName()).log(Level.SEVERE, "Cannot retrieve the images.", ex);
        }
    }

In my jsp, there's a large form (as my "images" collection is). Here, I use the data from the "images" collection. For each image, in the first column, a thumbnail and in the second column, a file input. I'm trying to perform multiple updates through this (single) form.

<form method="post" action="../admin/lstimg" enctype="multipart/form-data">
<table>
    <thead>
        <tr>
            <th>Thumb</th>
            <th>to update</th>
        </tr>
    </thead>
    <tbody>
        <c:forEach items="${images}" var="image">
            <tr>
                <td>
                    <img src="myURL" height="100"/>
                </td>
                <td>
                    <input type="file" name="${image.thumb}"/>
                </td>
            </tr>
        </c:forEach>
    </tbody>
</table>
<input type="submit" value="Proceed" />
</form>

My first question is : As I need the new image to have the same "image.thumb" (which is the Key), I set the file input name to the current "image.thumb" but I was wondering if, I'll have some issues to retrieve my file on my computer? As my file on my computer has its own path/filename and the one on my server has another path/UUID (Key).

EDIT (05/16/2012) : As the AWS doesn't ask for the file path (to be manually set), no issue with this.

How the form looks ( Web Browser )

After I choosed the image(s) I need to update/change, I click on the Submit button (Proceed).

  1. I get all the Parts (file inputs) and store them in a collection "allParts".
  2. I check every part of the "allParts" collection, if the file size is between 1b and 10Mo, the part is added to the "selectedParts" collection.
  3. Here, I don't know how to do what I want to do... All my images are hosted in my Amazon S3 Server. I use the Jets3t Toolkit. For the first upload, I call the buildPostForm method (below) and it does everything.
S3Service.buildPostForm(String bucketName, String key, ProviderCredentials credentials, Date expiration, String[] conditions, String[] inputFields, String textInput, boolean isSecureHttp);

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException
    {
        try
        {
            ImageCheck.initAS3(request);
            ImageCheck.update(request, response);
        } 

        catch (S3ServiceException ex) 
        {
            Logger.getLogger(ListImageServlet.class.getName()).log(Level.SEVERE, null, ex);
        } 

        catch (ServiceException ex) 
        {
            Logger.getLogger(ListImageServlet.class.getName()).log(Level.SEVERE, null, ex);
        } 

        catch (NoSuchAlgorithmException ex) 
        {
            Logger.getLogger(ListImageServlet.class.getName()).log(Level.SEVERE, null, ex);
        } 

        catch (IllegalStateException ex) 
        {
            Logger.getLogger(ListImageServlet.class.getName()).log(Level.SEVERE, null, ex);
        }

        response.sendRedirect("../admin/lstimg");
    }

My second question is : To upload/change a file through the servlet, as I use my own form, I need to set the File to my S3Object, but I don't know what I can do with my "selectedParts"? Is there a way to change a Part to a File? Do I have to use something else than Part?

EDIT (05/16/2012) : My solution

Sorry for my english. Thanks by advance for your suggestions, advices, answers. If you have any question regarding the code or if you need some more, don't hesitate.

I was so focused on S3Object#S3Object(java.io.File) that I didn't realized I could use StorageObject#setDataInputStream .

As I set the old image Key to the new image Key, my new problem is the web browser which keeps in cache the old image. I spent a lot of time on this, thinking my function wasn't running correctly, even if I didn't even know where I could have made a mistake; but I went on my AWS and checked the 'Last Modified' value that had been successfully updated. I cleared my cache and I figured out everything was ok from the very beginning.

If it can help some, I provide the functions I did which permit to change several S3Object through a single form. If you have any questions, suggestions or if you need any comment on that code, I'm here.

public static void upload(HttpServletRequest request, Part part, S3Service s3Service, S3Bucket s3Bucket) 
        throws S3ServiceException, NoSuchAlgorithmException, IOException
{
    S3Object s3Object = new S3Object();
    s3Object.setDataInputStream(part.getInputStream());
    s3Object.setKey(part.getName());
    s3Object.setContentLength(part.getSize());
    s3Object.setAcl(AccessControlList.REST_CANNED_PUBLIC_READ);
    s3Service.putObject(s3Bucket, s3Object);
}

        public static void update(HttpServletRequest request) 
        throws S3ServiceException, ServiceException, NoSuchAlgorithmException, IOException, IllegalStateException, ServletException
{
    AWSCredentials awsCredentials = (AWSCredentials) request.getSession().getAttribute("awsCredentials");
    S3Service s3Service = (S3Service) request.getSession().getAttribute("s3Service");
    S3Bucket s3Bucket = (S3Bucket) request.getSession().getAttribute("s3Bucket");
    String bucketName = (String) request.getSession().getAttribute("bucketName");

    String prefix = "uploads/";
    String delimiter = null;
    S3Object[] filteredObjects = s3Service.listObjects(bucketName, prefix, delimiter);

    Collection<Part> allParts = request.getParts();
    Collection<Part> selectedParts = new ArrayList<Part>();
    Collection<String> keys = new ArrayList<String>();

    for (Part part : allParts)
    {
        if (part.getSize()>1 && part.getSize()<10485760)
        {
            selectedParts.add(part);
        }
    }

    for (int o = 0; o < filteredObjects.length; o++)
    {
        String keyObject = filteredObjects[o].getName();
        keys.add(keyObject);
    }

    if (selectedParts.size() > 0)
    {
        for (Part part : selectedParts)
        {
            if (keys.contains(part.getName()))
            {
                s3Service.deleteObject(s3Bucket, part.getName());
                upload(request, part, s3Service, s3Bucket);
            }

            else
            {
                selectedParts.remove(part);
            }
        }
    }

    else
    {
        String ex = "No file to update.";
        exceptions.add(ex);
    }
}

Here's the javadoc of Part . The name of the input field is available by Part#getName() .

String name = part.getName();

The content of the uploaded file is available by Part#getInputStream() .

InputStream content = part.getInputStream();

You can read it and write it to an arbitrary OutputStream the usual Java I/O way . For example, a FileOutputStream . I'm however not sure what you ultimately need to do with the File . If your sole purpose is to store it in the DB, just store the InputStream directly instead of saving it on disk first.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM