Sending Images to the client from tomcat server - image

I am building a framework for e-commerce site. I have used jersey to create REST APIs. I need to send images to the clients as per the request.
How can I do so from my application server as Tomcat and jersey as REST API?
Since I am new to this, I have no clue how to send images to an Android client when they are shown as item.

Every resource is identified by the URI, client will ask for a particular image or a bunch of images by quering the URL, So you just need to expose a service, following service is an example to send single image to client.
#GET
#Path("/images/{imageId}")
#Produces("image/png")
public Response downloadImage(#PathParam("imageId") String imageId) {
MultiMediaDB imageDb = new MultiMediaDB();
String filePath = imageDb.getImage(imageId);
File file = new File(filePath);
ResponseBuilder response = Response.ok((Object) file);
response.header("Content-Disposition",
"attachment; filename=\"fileName.png\"");
return response.build();
}
MultiMediaDB is my custom class to get the file location from the DB, you can hardcode it as of now for testing purpose like D:\server_image.png.
You need to mention Content-Disposition as an attachment so that file will not be downloaded, instead attached to the form. In android you just need to read inputstream from a HttpURLConnection object and send that to bitmap as shown below
URL url = new URL(BaseUrl + "/images/" + imageId);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.connect();
iStream = urlConnection.getInputStream();
bitmap = BitmapFactory.decodeStream(iStream);
The you can set that bitmap to imageview or what ever you have as a container.

Related

Tomcat Performance with Spring Boot API for File Upload

I have a Spring boot API and one of the endpoints allows users to upload video's. Now My controller basically takes the file as a MultiPart file and then I store it in a temp folder accessible to tomcat. Once I have it stored on Disk, I then push the video to an S3 bucket.
Now to me anyway, this seems to be less than optimal, Like if I wanted to have a 100 or a 1000 users upload at once it seems really non performant to write the files to disk first.
As a little background I'm storing it on disk with the intention that if there is a issue pushing to S3 I can retry
The below code might show what I'm doing better than the above:
public Video addVideo(#RequestParam("title") String title,
#RequestParam("Description") String Description,
#RequestParam(value = "file", required = true) MultipartFile file) {
this.amazonS3ClientService.uploadFileToS3Bucket(file, title, description));
}
Method for storing Video file:
String fileNameWithExtenstion = awsS3FileName + "." + FilenameUtils.getExtension(multipartFile.getOriginalFilename());
//creating the file in the server (temporarily)
File file = new File(tomcatTempDir + fileNameWithExtenstion);FileOutputStream fos = new FileOutputStream(file);
fos.write(multipartFile.getBytes());
fos.close();PutObjectRequest putObjectRequest = new PutObjectRequest(this.awsS3Bucket, awsS3BucketFolder + UnigueId + "/" + fileNameWithExtenstion, file);
if (enablePublicReadAccess) {
putObjectRequest.withCannedAcl(CannedAccessControlList.PublicRead);
}
// Upload a file as a new object with ContentType and title
specified.amazonS3.putObject(putObjectRequest);
//removing the file created in the server
file.delete();
So my question is....is there a better way in Tomcat to:
A) Take in a file via a controllerB) Push to S3
There is no other way to do it with multipart. The problem with multipart that to properly segement parts from the requst they need sometimes skipped or be repeatable. That is impossible within memory w/o having memory to explode. Therefore, Commons FileUpload caches them on disk after a certain threshold is reached.
Multipart requests are the worst way for that. I highly recommend to use either PUT or POST with content type application/octet-stream. You can take the bare request input stream and pass to HttpClient to stream to your backend server. I did this already 5 years ago and it works for gigabytes. I have posted the solution in the Apache HttpClient mailing list.
There is one possibility how this could work under specific conditions:
All parts are in the correct physical order you want to read
Your write to a backend is fast enough to sustain the read from the front
Consume the root part and then go over to the next physical one, process the request body lazily. JAX-WS RI (Metro) has a very nice handling of multipart requests for XOP/MTOM. Learn from that because you won't be able to make it any better.
Perhaps you can try to direct stream the input stream from your MultipartFile to S3.
Consider the following uploadFileToS3Bucket method:
public PutObjectResult uploadFileToS3Bucket(InputStream input, long size, String title, String description) {
// Indicate the length of the information to avoid the need to compute it by the AWS SDK
// See: https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/s3/model/PutObjectRequest.html#PutObjectRequest-java.lang.String-java.lang.String-java.io.InputStream-com.amazonaws.services.s3.model.ObjectMetadata-
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(size); // rely on Spring implementation. Maybe you probably also can use input.available()
// compute the object name as appropriate
String key = "...";
PutObjectRequest putObjectRequest = new PutObjectRequest(
this.awsS3Bucket, key, input, objectMetadata
);
// The rest of your code
if (enablePublicReadAccess) {
putObjectRequest.withCannedAcl(CannedAccessControlList.PublicRead);
}
// Upload a file as a new object with ContentType and title
return specified.amazonS3.putObject(putObjectRequest);
}
Of course, you need to provide the service the input stream obtained from the client request associated with the MutipartFile object:
public Video addVideo(
#RequestParam("title") String title,
#RequestParam("Description") String Description,
#RequestParam(value = "file", required = true) MultipartFile file) {
try (InputStream input = file.getInputStream()) {
this.amazonS3ClientService.uploadFileToS3Bucket(input, file.getSize(), title, description));
}
}
Probably you can also play with the getBytes method of MultipartFile and create a ByteArrayInputStream to perform the operation.
In addVideo:
byte[] bytes = file.getBytes();
In uploadFileToS3Bucket:
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(bytes.length);
PutObjectRequest putObjectRequest = new PutObjectRequest(
this.awsS3Bucket, key, new ByteArrayInputStream(bytes), objectMetadata
);
I would prefer the first solution, but try to determine which option offers you the best performance.

I need the Jersey Multipart Client and server code to Upload more than one file.?

I need the Jersey Multipart Client to Upload more than one file.
I am able to upload a Single file but how can i upload more than one file.
In the client i set the two filedatabody parts.
final FileDataBodyPart filePart = new FileDataBodyPart("file", new File("path"));
FormDataMultiPart formDataMultiPart = new FormDataMultiPart();
FileDataBodyPart filePart2 = new FileDataBodyPart("file", new File("path2"));
final FormDataMultiPart multipart =
(FormDataMultiPart) formDataMultiPart.field("foo", "bar").bodyPart(filePart).bodyPart(filePart2);
How to write the server side code.
The "file" you're using here new FileDataBodyPart("file", new File("path2")); is the name of the body part. If you are going to name them the same (which is allowed), then use a List for your parameter type
public Response upload(#FormDataParam("file") List<InputSream> files)
Otherwise if you want to change the name of one of the parts, then just add another #FormDataParam parameter using that part's name
public Response upload(#FormDataParam("file1") InputStream file1,
#FormDataParam("file2") InputStream file2)

Sending gzipped data over HTTPS

I need to send a gzipped byte array over HTTPS. I searched the web and only thing ı can found is SharpGIS.GZipWebClient.
However, the problem is - this third party solution only works with WebClient which allow you to send only String data.
(I'm on Windows Phone 8. Most of the WebClient methods do not exist.)
Any ideas to solve this problem?
Edit:
This is how I tried the POST JSON data over HTTPS using SharpGIS;
WebClient webClient = new SharpGIS.GZipWebClient();
webClient.Headers["Accept-Encoding"] = "gzip";
var uri = new Uri(pUrl, UriKind.Absolute);
webClient.UploadStringCompleted += new UploadStringCompletedEventHandler(wc_UploadStringCompleted);
webClient.UploadStringTaskAsync(uri, jsonAsString);
But it doesn't compresses the string as well(as using OpenWriteSync method).
You write the post data in the OpenWriteCompleted handler, like this:
void webClient_OpenWriteCompleted(object sender, OpenWriteCompletedEventArgs e)
{
Stream s = e.Result;
s.Write(jsonAsByteArray, 0, jsonAsByteArray.Length);
s.Flush();
s.Close();
}
You should also add the appropriate error handling.

Capture current JSF page content

I want to capture the current page and send it to an application that converts it to pdf.
This is what I am using:
FacesContext facesContext=FacesContext.getCurrentInstance();
HttpServletResponse response = (HttpServletResponse)
facesContext.getExternalContext().getResponse();
HttpServletRequest request = (HttpServletRequest) facesContext.getExternalContext().getRequest();
// RequestPrinter.debugString();
response.reset();
// download a pdf file
response.setContentType("application/pdf");
response.setHeader("Content-Disposition","attachment;filename="+new Date().toString()+".pdf");
prince.setVerbose(true);
prince.setLog(logFile);
try{
//getPath() to the page the user is currently on
URL pagePath=new URL(this.getPath());
URLConnection urlConnection = pagePath.openConnection();
urlConnection.setDoOutput(true);
int length = urlConnection.getContentLength();
//Lets use inputStream
BufferedInputStream bis=new BufferedInputStream(urlConnection.getInputStream());
response.setContentLength(length);
//this.getPageUsingJSoup().data().getBytes();
//call prince and pass params for inputstream outputStream
prince.convert(bis,response.getOutputStream());
urlConnection.getInputStream().close();
}catch(MalformedURLException mu){
mu.printStackTrace();
}
catch(IOException ioe){
ioe.printStackTrace();
}
facesContext.responseComplete();
Since the website requires authentication, the pdf generated is the loging error page.
Is there a way to capture the page's content that uses the current user's session?
Thank you in advance.
Just request the page in the same HTTP session as the current request. If your webapp supports URL rewriting (as by default), then just append session ID as jsessionid path fragment:
String sessionId = ((HttpSession) externalContext.getSession()).getId();
InputStream input = new URL("http://localhost:8080/context/page.jsf;jsessionid=" + sessionId).openStream();
// ...
Or if your webapp doesn't accept URL rewriting, but accepts cookies only, then set it as a request cookie the usual way:
URLConnection connection = new URL("http://localhost:8080/context/page.jsf").openConnection();
connection.setRequestProperty("Cookie", "JSESSIONID=" + sessionId);
InputStream input = connection.getInputStream();
// ...
Note that I removed setDoOutput() since you don't seem to be interested in performing a POST request.
I do not know how to capture the page's content using the current user's session, but I can suggest another way to do it - you could move the pdf conversion logic inside a Selenium test-case and use the test-case to navigate and login to the page requiring authentication. After the automated tc has logged in, you could call your pdf conversion logic...?
Yes of course there is. You are sending this content, so you have it. You should store the Content Object. If you dont have it, inspect your byte streams. The content should be there ;)
There of couple of websites which allow you to convert the entire page to pdf and save it as .pdf file. Try out the site http://pdfcrowd.com/ Hope this helps you.

Windows Azure REST API MediaLink

I'm trying to use the API REST of Windows Azure for creating a virtual machine deployment. However, I've got a problem when trying to specify an OS image in the following XML file:
<Deployment xmlns="http://schemas.microsoft.com/windowsazure" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Name>SomeName</Name>
<DeploymentSlot>Production</DeploymentSlot>
<Label></Label>
<RoleList>
<Role i:type="PersistentVMRole">
<RoleName>SomeName</RoleName>
<OsVersion i:nil="true"/>
<RoleType>PersistentVMRole</RoleType>
<ConfigurationSets>
<ConfigurationSet i:type="WindowsProvisioningConfigurationSet">
<ConfigurationSetType>WindowsProvisioningConfiguration</ConfigurationSetType>
<ComputerName>SomeName</ComputerName>
<AdminPassword>XXXXXXXXXX</AdminPassword>
<EnableAutomaticUpdates>true</EnableAutomaticUpdates>
<ResetPasswordOnFirstLogon>false</ResetPasswordOnFirstLogon>
</ConfigurationSet>
<ConfigurationSet i:type="NetworkConfigurationSet">
<ConfigurationSetType>NetworkConfiguration</ConfigurationSetType>
<InputEndpoints>
<InputEndpoint>
<LocalPort>3389</LocalPort>
<Name>RemoteDesktop</Name>
<Protocol>tcp</Protocol>
</InputEndpoint>
</InputEndpoints>
</ConfigurationSet>
</ConfigurationSets>
<DataVirtualHardDisks/>
<Label></Label>
<OSVirtualHardDisk>
<MediaLink>¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿???????????????</MediaLink>
<SourceImageName>¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿??????????????????</SourceImageName>
</OSVirtualHardDisk>
</Role>
</RoleList>
</Deployment>`
I need the MediaLink (URI of the OS image) and the SourceImageName (Canonical name of the OS image). My question is, the web portal provides several PREDEFINED images but I cannot determine the URI and the canonical name of them. Will I be forced to create my own OS image and upload it to any of the storage services under my Windows Azure account?
To get these parameters, you could perform List OS Images Service Management API operation on your subscription.
UPDATE
Please discard some of my comments below (sorry about those). I was finally able to create a VM using REST API :). Here're some of the things:
<MediaLink> element should specify the URL of the VHD off of which your VM will be created. It has to be a URL in one of your storage account in the same subscription as your virtual machine cloud service. So for this, you could specify a URL like: https://[yourstorageaccount].blob.core.windows.net/[blobcontainer]/[filename].vhd where [blobcontainer] would be the name of the blob container where you would want the API to store the VHD while the [filename] is any name that you want to give to your VHD. What REST API does is that it copies the source image specified in the <SourceImageName> and saves it at the URI specified in the <MediaLink> element.
Make sure that your Service and Storage Account where your VHD will be stored are in the same data center/affinity group. Furthermore that data center should be able to support Virtual Machines. It turns out that not all data centers support Virtual Machines.
Order of XML element is of utmost importance. You move one element up or down would result in 400 error.
Based on my experimentation, here's the code:
private static void CreateVirtualMachineDeployment(string subscriptionId, X509Certificate2 cert, string cloudServiceName)
{
try
{
string uri = string.Format("https://management.core.windows.net/{0}/services/hostedservices/{1}/deployments", subscriptionId, cloudServiceName);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = "POST";
request.ContentType = "application/xml";
request.Headers.Add("x-ms-version", "2013-03-01");
request.ClientCertificates.Add(cert);
string requestPayload = #"<Deployment xmlns=""http://schemas.microsoft.com/windowsazure"" xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"">
<Name>[SomeName]</Name>
<DeploymentSlot>Production</DeploymentSlot>
<Label>[SomeLabel]</Label>
<RoleList>
<Role i:type=""PersistentVMRole"">
<RoleName>MyTestRole</RoleName>
<OsVersion i:nil=""true""/>
<RoleType>PersistentVMRole</RoleType>
<ConfigurationSets>
<ConfigurationSet i:type=""WindowsProvisioningConfigurationSet"">
<ConfigurationSetType>WindowsProvisioningConfiguration</ConfigurationSetType>
<ComputerName>[ComputerName]</ComputerName>
<AdminPassword>[AdminPassword - Ensure it's strong Password]</AdminPassword>
<AdminUsername>[Admin Username]</AdminUsername>
<EnableAutomaticUpdates>true</EnableAutomaticUpdates>
<ResetPasswordOnFirstLogon>false</ResetPasswordOnFirstLogon>
</ConfigurationSet>
<ConfigurationSet i:type=""NetworkConfigurationSet"">
<ConfigurationSetType>NetworkConfiguration</ConfigurationSetType>
<InputEndpoints>
<InputEndpoint>
<LocalPort>3389</LocalPort>
<Name>RemoteDesktop</Name>
<Protocol>tcp</Protocol>
</InputEndpoint>
</InputEndpoints>
</ConfigurationSet>
</ConfigurationSets>
<DataVirtualHardDisks/>
<Label></Label>
<OSVirtualHardDisk>
<MediaLink>https://[storageaccount].blob.core.windows.net/vhds/fb83b3509582419d99629ce476bcb5c8__Microsoft-SQL-Server-2012SP1-Web-CY13SU04-SQL11-SP1-CU3-11.0.3350.0.vhd</MediaLink>
<SourceImageName>fb83b3509582419d99629ce476bcb5c8__Microsoft-SQL-Server-2012SP1-Web-CY13SU04-SQL11-SP1-CU3-11.0.3350.0</SourceImageName>
</OSVirtualHardDisk>
</Role>
</RoleList>
</Deployment>";
byte[] content = Encoding.UTF8.GetBytes(requestPayload);
request.ContentLength = content.Length;
using (var requestStream = request.GetRequestStream())
{
requestStream.Write(content, 0, content.Length);
}
using (HttpWebResponse resp = (HttpWebResponse)request.GetResponse())
{
}
}
catch (WebException webEx)
{
using (var streamReader = new StreamReader(webEx.Response.GetResponseStream()))
{
string result = streamReader.ReadToEnd();
Console.WriteLine(result);
}
}
}
Hope this helps!

Resources