I am new to programming and couldn't find an answer to fit my question, and am unsure where else to turn. As stated in the title, I'm looking to download a file using HtmlUnit in Java, but the download button on the page has no href or onclick I can access. Button follows:
<button class="btn btn-download btn-primary pull-right" id="eta_download" style="display: block;">
<span class="glyphicon glyphicon-download-alt"></span>
</button>
clicking this button causes a normal browser to do some processing and loading for a short amount of time, then open a tab that triggers the download of a gzip file containing a tiff satellite image. I am doing this in a Swing app.
The site I need to download gzipped tiff from
Can anyone help me get this to work?
My code follows:
// Call from whithin new Thread. Get the download
private void getDownload(String latitude, String longitude, String start, String end) throws Exception
{
// Create the browser
final WebClient webClient = new WebClient(BrowserVersion.CHROME);
// Report to user. Loading page...
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
reportLabel.setText("Loading EEFLUX...");
}
});
// Load page
HtmlPage page = webClient.getPage("https://eeflux-level1.appspot.com/");
// Report to user change in state
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
reportLabel.setText("Filling in values");
}
});
// Get Latitude, Lomgitude and Date Fields
HtmlInput latitudeField = (HtmlInput) page.getElementById("latitude");
HtmlInput longitudeField = (HtmlInput) page.getElementById("longitude");
HtmlInput date_start_Field = (HtmlInput) page.getElementById("date_start");
HtmlInput date_end_Field = (HtmlInput) page.getElementById("date_end");
// Set the values of fields to that passed into method
latitudeField.setAttribute("value", latitude);
longitudeField.setAttribute("value", longitude);
date_start_Field.setAttribute("value", start);
date_end_Field.setAttribute("value", end);
// Get the Search "Button" then click
HtmlAnchor search = (HtmlAnchor) page.getHtmlElementById("searchForImages");
page = search.click();
// wait for Javascripts jobs to finish
JavaScriptJobManager manager = page.getEnclosingWindow().getJobManager();
for (int i = 0; manager.getJobCount() > 7; i++)
{
final int j = i;
// Report to user change in state
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
reportLabel.setText("Loading after Search: " + j);
}
});
Thread.sleep(1000);
}
// Get the list of regions Satellites captured and click to open dropdown
HtmlDivision image_dropdown = (HtmlDivision) page.getElementById("image_dropdown");
image_dropdown.click();
// Get the list of regions
HtmlUnorderedList region_list = (HtmlUnorderedList) image_dropdown.getLastElementChild();
// get iterator for list
Iterator<DomElement> web_list = region_list.getChildElements().iterator();
// Report to user change in state
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
reportLabel.setText("Accessing region list");
}
});
// for each Element, download Actual ET image (and later Grass Reference)
while(web_list.hasNext())
{
DomElement region = web_list.next();
System.out.println(region.getTextContent());
HtmlPage page2 = region.click();
// wait for Javascripts jobs to finish
manager = page2.getEnclosingWindow().getJobManager();
for (int i = 0; manager.getJobCount() > 2; i++)
{
final int j = i;
// Report to user
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
reportLabel.setText("Loading Image Type page: " + j);
}
});
System.out.println(manager.getJobCount());
Thread.sleep(1000);
}
// Get the Actual ET download Button
HtmlButton ETButton = page2.getHtmlElementById("eta_download");
// Get the Download Page????
HtmlPage page3 = ETButton.click();
UnexpectedPage download_ET = new UnexpectedPage(page3.getWebResponse(), page3.getEnclosingWindow());
// Get the Stream
GZIPInputStream in_ET = (GZIPInputStream) download_ET.getWebResponse().getContentAsStream();
// Try writing the stream (to standard out for now)
try
{
byte[] buffer = new byte[2048];
int len;
while((len = in_ET.read(buffer)) != -1)
{
System.out.write(buffer, 0, len);
}
}
finally
{
// Close the stream
in_ET.close();
}
// just do one till this works
break;
}
}
This is a good start :)
I looked at the request being sent when clicking the button :
As you can see there are several parameters being sent (latitude, longitude, date_end, image_id). In the response, you have the download URL.
This request is generated with some Javascript code, probably this :
function downloadImage(divName,urlProduct){
$(document).ready(function(){
$(divName).on('click', function(){
onlyshowLoading();
$.ajax({
url: urlProduct,
type: "POST",
data: JSON.stringify({
"lat": $('#latitude').val(),
"lng": $('#longitude').val(),
"date_info": $('#date_start').val() + ' to ' + $('#date_end').val(),
'image_id': $("#dropdown:first-child").text().split(" / ")[1],
}),
dataType: 'json',
cache: true,
error: function(){
AjaxOnError();
},
success: function(data){
AjaxOnSuccess();
if (typeof ETa_adjusted == "undefined" || ETa_adjusted == null){
$("#ETrF_adjusted").hide();
$("#EToF_adjusted").hide();
$("#ETa_adjusted").hide();
$("#etrF_adj_download").hide();
$("#etoF_adj_download").hide();
$("#eta_adj_download").hide();
} else{
$("#ETrF_adjusted").show();
$("#EToF_adjusted").show();
$("#ETa_adjusted").show();
$("#etrF_adj_download").show();
$("#etoF_adj_download").show();
$("#eta_adj_download").show();
}
var key = Object.keys(data);
typeName = data[key]
window.open(typeName.url, '_blank');
}
});
});
})
}
So it's possible that HtmlUnit is unable to execute this code, because of Jquery or whatever.
You could create your own WebRequest object, and reproduce the Javascript logic, then you will get the download URL.
It's an interesting subject, if you want to know more I'm in the middle of writing an ebook about web scraping with Java : Java Web Scraping Handbook
Related
This makes me really curious.There is a button which sends a simple post request by ajax on a jsp page,and I use a RESTFUL method to handle this request,but that method will be executed twice or three times.This will only happen on CentOS 7.3,on my laptop I use windows10, multi-thread will not happen.I have searched on Google but nothing helpful
has been found.Here are the codes:
asset.jsp:
<button class="btn btn-default btn-sm" title="allDoMi">
<i class="fa fa-arrow-down">allDoMi</i>
</button>
$("button[title='allDoMi']").click(function () {
var dataparam = "version=1";
if (!confirm('confirm all DoMi?'))
return;
//ajax request
$.ajax({
contentType: "application/json",
url: serviceurl.baseurl + "/asset/doMiAction",
data: dataparam,
beforeSend: function () {
//do nothing
},
error: function () {
//do nothing
},
success: function (info) {
//do nothing
}
});
});
Asset.java
#Service
#Path("/asset")
public class AssetRest {
#Path("/doMiAction")
#POST
#Produces({ MediaType.APPLICATION_JSON,
MediaType.APPLICATION_XML })
public RestfulResult doMiAction(#FormParam("version") String
version) {
logger.info("doMiAction method began....");
//package data for duMiSyncDtos,here only for test
List<DuMiSyncDto> duMiSyncDtos =new List<>();
//this url is for http simulation using HttpURLConnection
final String dumiUrl = "http://someip/someurl";
final Map<String, List<DuMiSyncDto>> map;
//only continue when list is not empty
if (!duMiSyncDtos.isEmpty()) {
//this method used for sync data in a certain order
map = groupList(duMiSyncDtos);
SortedSet<String> ss = new TreeSet<>(map.keySet());
final Iterator<String> iter = ss.iterator();
final ScheduledExecutorService ticker = Executors.newSingleThreadScheduledExecutor();
logger.info("NEW A SINGLETHREADSCHEDULEDEXECUTOR");
//retrieve data from a .property file,I set it 20000,therefore the job will be executed in every 20 seconds
final int DELAY = NumberUtils.toInt(WebUtils.getConfigValue("dumi.delay"));
ticker.scheduleWithFixedDelay(new Runnable() {
private int count;
public void run() {
logger.info("BEGIN RUN METHOD:"+System.identityHashCode(AssetRest.this));
if(iter.hasNext()) {
try {
List<DuMiSyncDto> value = map.get(iter.next());
//this method used for simulating a httprequest using HttpURLConnection to invoke a remote service to get the result info which forms in a JSON string format
String resultmsg = getDuMiReturnMessage(dumiUrl,value);
if(resultmsg!=null && !resultmsg.contains("\"code\":\"0000\"")) {
logger.info("Return code is "+resultmsg+",the sync work will be terminated.");
ticker.shutdown();
return;
}
//this method used for showing some useful infomation on the console using log4j
showSyncInfomation(value);
//this method used for count how many items have been synchronized successfully
int currentcount = getSyncCount(resultmsg);
count += currentcount ;
logger.info("current sync data:"+currentcount+",summing data"+count+"");
} catch (Exception e) {
logger.error("method[doMiAction]...executing schedule:",e);
}
} else {
ticker.shutdown();
}
}
}, 0, DELAY, TimeUnit.MILLISECONDS);
}
}
After I click the button,all the log info will be shown on Putty console for two or three times,yet I have clicked that button for only ONCE!I have tested for several times,it will happen,but in windows on my laptop,this will not happen at all.Here is a detail might be help:previously the implementation for timed execution is not like this,it has been written like :
for(DuMiSyncDto dto:duMiSyncDtoList){
//do the business code
Thread.sleep(20000);
}
Because there is database synchronization from the remote service,I need to control the interval time not too soon between every two operations:execute in every 20 seconds and 100 data at a time.In this situation,the multi-thread problem occurs,I thought it may be the for loop which aroused so I change the way using a JDK API instead but issues were still there.So WHY all of these?
---------------------------first edit------------------------------------------
private int getSyncCount(String resultmsg) {
int count = 0;
JSONObject obj = JSONObject.fromObject(resultmsg);
String message = obj.getString("message");
if(!WebUtils.isEmpty(message)) {
String[] arr = message.split(" ");
if(arr!=null && arr.length>1) {
count += Integer.parseInt(arr[1].trim());
}
}
logger.info("currentThreadName:"+Thread.currentThread().getName());
return count;
}
Notice in this method,I log the current thread name,and it shows :
...
currentThreadName:pool-1-thread-1
currentThreadName:pool-2-thread-1
currentThreadName:pool-3-thread-1
...
when there are 3 threads.
i have implemented a webview in my android app and trying to highlight or to mark element when user click in the layout.
The webview is initialized as following :
myWebView.getSettings().setJavaScriptEnabled(true);
//myWebView.getSettings().setGeolocationEnabled(true);
//myWebView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
myWebView.getSettings().setBuiltInZoomControls(true);
myWebView.getSettings().setDomStorageEnabled(true);
myWebView.setWebViewClient(new WebViewController());
Trying to mark the element which is clicked by user for example like in this screenshot :
Selection with dot
I'm getting all the page divs via jsoup :
doc = Jsoup.connect(url).get();
final Elements alldivs = doc.select("div");
ArrayList<String> list = new ArrayList<String>();
for (org.jsoup.nodes.Element e : alldivs) {
if (!e.id().equals(""))
list.add(e.id());
}
But how to mark the selection as the photo above, and after that select marked content from div id.
How can make some thing like this ?
I'm using this javascript into webview to hightlight the selection but how to get the clicked element programmatically like : id of selected div or other values
public class MyWebViewClient extends WebViewClient {
#Override
public void onPageFinished(WebView view, String url) {
view.loadUrl("javascript: "
+ "Object.prototype.each = function (fn, bind) {\n" +
" console.log(bind);\n" +
" for (var i = 0; i < this.length; i++) {\n" +
" if (i in this) {\n" +
" fn.call(bind, this[i], i, this);\n" +
" }\n" +
" }\n" +
" };\n" +
"\n" +
" var _addListener = document.addEventListener || document.attachEvent,\n" +
" _eventClick = window.addEventListener ? 'click' : 'onclick';\n" +
"\n" +
" var elements = document.getElementsByTagName(\"div\");\n" +
"\n" +
" elements.each(function (el) {\n" +
" _addListener.call(el, _eventClick, function () {\n" +
// todo process the clicked div element
" el.style.cssText = \"border-color: black;border-style: dashed;\"\n" +
" }, false);\n" +
" })");
}
}
If I understand correctly, you want to get some information from the HTML component into the native side.
According to this answer, it is not possible to pass arbitrary objects to Java, but at least you can pass the HTML code of the clicked node and then parse it natively.
This code based on yours does exactly that.
MainActivity.java: I guess this is pretty self-explanatory. The only thing I did different from you is to get the Javascript code from a separate file, so it's easier to maintain.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final WebView myWebView = findViewById(R.id.webView);
final WebSettings settings = myWebView.getSettings();
settings.setJavaScriptEnabled(true);
myWebView.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
final String injectedJs = "javascript:(function(){" + injectedJs() + "})()";
myWebView.loadUrl(injectedJs);
}
});
myWebView.addJavascriptInterface(
new Object() {
#JavascriptInterface
public void onClick(String param) {
Toast.makeText(MainActivity.this, param, Toast.LENGTH_LONG).show();
}
},
"appHost"
);
myWebView.loadUrl("https://google.com");
}
// Javascript code to inject on the Web page
private String injectedJs() {
BufferedReader stream = null;
StringBuilder jsBuilder = new StringBuilder();
try {
stream = new BufferedReader(new InputStreamReader(getAssets().open("js.js")));
String line;
while ((line = stream.readLine()) != null) {
jsBuilder.append(line.trim());
}
return jsBuilder.toString();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return "";
}
}
js.js: The base of this part is your code. Keep in mind that since injectedJs() removes all line markers, every statement needs to finish, including comments, hence the /*...*/ instead of //
/* Keep track of the last clicked element so we can un-highlight it */
var lastSelectedItem = null;
var showHighlighted = function(/* HTML element */view, /*boolean */highlighted) {
if (view) {
view.style.cssText = highlighted? 'border-color: black;border-style: dashed;' : '';
}
};
/* This new method, _addEventListener and _eventClick are the same as yours */
Object.prototype.each = function (fn, bind) {
for (var i = 0; i < this.length; i++) {
if (i in this) {
fn.call(bind, this[i], i, this);
}
}
};
var _addListener = document.addEventListener || document.attachEvent,
_eventClick = window.addEventListener ? 'click' : 'onclick';
/* I changed the element selection criteria, but you can change it back easily.
I am adding event listeners all the leaf elements in the DOM. */
var elements = document.body.getElementsByTagName('*');
elements.each(function (el) {
if (el.children.length == 0) {
_addListener.call(el, _eventClick, function () {
/* First, deal with the previously selected item*/
showHighlighted(lastSelectedItem, false);
if (lastSelectedItem !== null) {
appHost.onClick(lastSelectedItem.outerHTML);
}
/* Update the selected item reference */
lastSelectedItem = el;
/* Finally, deal with the previously selected item*/
showHighlighted(lastSelectedItem, true);
appHost.onClick(el.outerHTML);
}, false);
}
});
consider the web view as a view of a web page. You need to configure the element inside that web view to send a request that would fire an intent in your android application, which is possible, but it would not work for multiple users unless you know the user in that web view and authenticate the users... the point is , it is very complicated if you want to send that request from web to the logical part of your app. Even if you can do it, it is not optimal and i discourage it.
On the other hand, what you can do if you insist on using web views is to complete the rest of your logical operations on the web view. Don't go back from the web view to the app java logic.
Normally web views are used to show something rather than to make actions on the app. (the action might be used on the same web view)
I hope you do get , I've tried to explain as much as possible.
I use oracle-adf via xml menu model. Now I want to start download file on one of itemNode and without redirect. I'm tried to define action attribute with method which invokes hidden button's method(this button has inner fileDownloadActionListener) through javascript. But it doesn't work. Is it correct? or there is other way to decide this problem? Or may be it is impossible at all?
Hidden button code:
<af:commandButton text="NONE"
id="downloadInstructionsBtn"
action=" "
visible="false"
clientComponent="true"
partialSubmit="false">
<af:fileDownloadActionListener filename="Инструкции пользователя"
contentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
method="#{pageFlowScope.vocsReportsRegionController.instructionsDownload}"/>
</af:commandButton>
Item node code:
<itemNode id="userInstructionsDownloadFlow" label="Инструкции пользователя"
focusViewId="#"
action="#{pageFlowScope.vocsReportsRegionController.invokeInstructionDownload}"
partialSubmit="true"
clientComponent="true"/>
Javascript cut:
function handleInstructionsDownload(event) {
event.preventUserInput();
var source = event.getSource().getParent();
var downloadBtn = source.findComponent("downloadInstructionsBtn");
var actionEvent = new AdfActionEvent(downloadBtn);
actionEvent.preventUserInput();
actionEvent.queue();
}
Methods' description:
public void invokeInstructionDownload(){
FacesContext context = FacesContext.getCurrentInstance();
ExtendedRenderKitService erks =
Service.getService(context.getRenderKit(),
ExtendedRenderKitService.class);
erks.addScript(context, "handleInstructionsDownload();");
}
public void instructionsDownload(FacesContext context,
OutputStream out) throws IOException{
File f = new File("C:\\Users\\apozdnyakov\\Downloads\\Типы_контроля_время.xlsx");
FileInputStream fis;
byte[] b;
try {
fis = new FileInputStream(f);
int n;
while ((n = fis.available()) > 0) {
b = new byte[n];
int result = fis.read(b);
out.write(b, 0, b.length);
if (result == -1)
break;
}
} catch (IOException e) {
e.printStackTrace();
}
out.flush();
}
I think there are a few possible reasons of your problem.
1- handleInstructionsDownload javascript function couldn't find the component, because of hierarchy of the jsf codes. You can add log in javascript function, for understanding that.
function handleInstructionsDownload(event) {
event.preventUserInput();
var source = event.getSource().getParent();
var downloadBtn = source.findComponent("downloadInstructionsBtn");
if (downloadBtn == null) {
console.log('The component is null!');
}
var actionEvent = new AdfActionEvent(downloadBtn);
actionEvent.preventUserInput();
actionEvent.queue();
}
If you see this log in javascript console you should control your hierarchy of jsf codes. And you should access to button by true component id.
2- invokeInstructionDownload java method couldn't find or call the javascript handleInstructionsDownload function. You can add log in the first line of the javascript function like that:
function handleInstructionsDownload(event) {
console.log('The js function fired!');
event.preventUserInput();
var source = event.getSource().getParent();
var downloadBtn = source.findComponent("downloadInstructionsBtn");
var actionEvent = new AdfActionEvent(downloadBtn);
actionEvent.preventUserInput();
actionEvent.queue();
}
If there is this log in console, you should change your javascript method calling. But I think your Java method is true :)
3- If these are not solutions of your problem, you can change your calling the hidden button like that.
Hidden button code:
<af:commandButton text="NONE"
id="downloadInstructionsBtn" action=" "
visible="false"
clientComponent="true"
partialSubmit="false"
binding="#{pageFlowScope.vocsReportsRegionController.downloadInstructionsBtn}">
<af:fileDownloadActionListener filename="Инструкции пользователя"
contentType="application/vnd.openxmlformats officedocument.spreadsheetml.sheet"
method="#{pageFlowScope.vocsReportsRegionController.instructionsDownload}"/>
</af:commandButton>
ManagedBean code:
private RichButton downloadInstructionsBtn;
public RichButton getDownloadInstructionsBtn() {
return downloadInstructionsBtn;
}
public void setDownloadInstructionsBtn(RichButton downloadInstructionsBtn) {
this.downloadInstructionsBtn = downloadInstructionsBtn;
}
public void invokeInstructionDownload(){
ActionEvent actionEvent = new ActionEvent((UIComponent) downloadInstructionsBtn);
actionEvent.queue();
}
it's as it says on the title
First of all, I would like to apologize if this is a dumb question =[
so basically i'm trying to send email through gwt. I have no idea how gwt mail work so instead I tried to use php way (which is more familiar to me) but I have no idea how to get this working.
So.. in my war folder I have my index.html and email.php that I've created. In my index.html, I have a form that calls my email.php.
<?php
// Check for empty fields
if(empty($_POST['name']) ||
empty($_POST['email']) ||
empty($_POST['message']) ||
!filter_var($_POST['email'],FILTER_VALIDATE_EMAIL))
{
echo "did you make sure to fill everything?";
return false;
}
$name = $_POST['name'];
$email = $_POST['email'];
$message = $_POST['message'];
// Create the email and send the message
$to = 'myemail#gmail.com'; // Add your email address inbetween the '' replacing yourname#yourdomain.com - This is where the form will send a message to.
$email_subject = "Website Contact Form: $name";
$email_body = "You have received a new message from your website contact form.\n\n"."Here are the details:\n\nName: $name\n\nEmail: $email\n\n Message:\n$message";
$headers = "From: noreply#somedomain.com\n"; // This is the email address the generated message will be from. We recommend using something like noreply#yourdomain.com.
$headers .= "Reply-To: $email_address";
mail($to,$email_subject,$email_body,$headers);
return true;
?>
above is my php code that I am currently using. However not only eclipse does not recognize php but also when I press my button, it only prints out this code and not running it.
Does this happen to anyone and can anyone please help me?
Thank you =]
What you can do:
Recreate the email form in GWT with FormPanel, or
create your own POST-Request in GWT with RequestBuilder
In both cases: the 'echo' statements that your PHP form returns will not be appended to the HTML page but returned to you as a String. You will have to decide what to do (tell the user? take other action) in your GWT code.
Recreate as form panel:
// create the textboxes of the form with their proper form names
TextBox tbName = new TextBox();
tbName.setName( "name" );
TextBox tbEmail = new TextBox();
tbEmail.setName( "email" );
TextBox tbMessage = new TextBox();
tbMessage.setName( "message" );
// create the form panel
final FormPanel emailFormPanel = new FormPanel();
// TODO: add the form panel to some kind of parent widget / ui object
emailFormPanel.setAction( "/contextRoot/path/to/email.php" );
emailFormPanel.setMethod( "POST" );
// add the textboxes to the form panel
emailFormPanel.add( tbName );
emailFormPanel.add( tbEmail );
emailFormPanel.add( tbMessage );
// create the form submit button
Button btnSubmit = new Button( "Submit", new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
emailFormPanel.submit();
}
} );
// create the formpanel handler for a successful submit
// any error message ("did you forget to ...") will be returned here
emailFormPanel.addSubmitCompleteHandler( new SubmitCompleteHandler() {
#Override
public void onSubmitComplete(SubmitCompleteEvent event) {
String errorString = event.getResults();
// TODO: decide what to do with a potential non-empty string
}
} );
Create your own POST-Request with RequestBuilder:
// create the textboxes of the form with their proper form names
final TextBox tbName = new TextBox();
final TextBox tbEmail = new TextBox();
final TextBox tbMessage = new TextBox();
// create the form submit button
Button btnSubmit = new Button( "Submit", new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
submitEmailFormRequestBuilder( tbName, tbEmail, tbMessage );
}
} );
// TODO: add textboxes and Submit-Button to the DOM-tree
submit the textbox values:
protected void submitEmailFormRequestBuilder(TextBox name, TextBox email, TextBox message) {
// create the request content in a way that the php script can read it:
// for every textbox the php textbox-name = user-value
StringBuilder requestData = new StringBuilder();
requestData.append( "name=" + name.getValue() );
requestData.append( "&email=" + email.getValue() );
requestData.append( "&message=" + message.getValue() );
// create the REST request callback
RequestCallback callback = new RequestCallback() {
#Override
public void onResponseReceived(Request request, Response response) {
String errorMessage = response.getText();
// TODO: handle potential error-message
}
#Override
public void onError(Request request, Throwable exception) {
// TODO: handle timeouts and other sending failures like cross-domain posting
}
};
// create the REST request as POST request
RequestBuilder build = new RequestBuilder( RequestBuilder.POST, "/contextRoot/email.php" );
try {
build.sendRequest( requestData.toString(), callback );
}
catch (RequestException e) {
// handle exception
}
}
Otherwise, if index.html is not your GWT starting page where the .nocache.js module is called, then you could include it in your GWT code with an IFrame.
Hi I want to stream videos in client app but videos are located in server app. I am using java Restlet and Jquery Ajax to connect client app to server app. Through Ajax call i am connecting to Restlet. I don't know how to send response to ajax after streaming video from server side, how ajax receives response and how to play video in browser. Can any one help me to handle this.
Here is my code
Html:
<button id="playVideo" class="btn-primary">PlayVideo</button>
<video id="videoTab" height="300" width="500" style="display: none" controls ></video>
Ajax Call to server
$('#playVideo').click(function (){
var jsonObj = {};
jsonObj.userId = "siva";
jsonObj.file = "sample.mp4";
//console.log("json obje :"+ JSON.stringify(jsonObj))
// Rest call to play videos.
$.ajax({
type : 'GET',
url : config.streamVideo,
//dataType : 'json',
data : JSON.stringify(jsonObj),
contentType : "application/json",
mimeType : "video/mp4",
processData : false,
crossDomain : true,
success : function(result) {
//console.log("login result : " + JSON.stringify(result));
if (result) {
console.log("success.....");
srcPath = "data:video/mp4;"+result;
$('#videoTab').attr('src', srcPath);
$('#videoTab').css('display', 'block');
$('#videoTab').attr('autoplay', true);
} else {
alert('failed...');
}
},
error : function(){
alert('error')
}
});
});
RestletCode:
#Get
public InputRepresentation handleRequest(Representation entity) throws IOException, ResourceException {
// Set response headers
Series<Header> responseHeaders = (Series<Header>) getResponse().getAttributes().get("org.restlet.http.headers");
if (responseHeaders == null) {
responseHeaders = new Series<Header>(Header.class);
getResponse().getAttributes().put("org.restlet.http.headers", responseHeaders);
}
responseHeaders.add(new Header("Access-Control-Allow-Origin", "*"));
logger.debug("+++++++++++++++++++Entered in play video restlet +++++++++++++++");
// Convert Rest type request to Servlet request
httpServletRequest = ServletUtils.getRequest(getRequest());
// Get Servlet context object.
sc = httpServletRequest.getServletContext();
// Get input file path.
logger.debug("------->getRealPath " + sc.getRealPath("/"));
String filePath = sc.getRealPath("/") + "WEB-INF\\data\\videos\\sample.mp4";
final File file = new File(filePath);
if (file.exists()) {
logger.debug("Requested file path : " + file.getAbsolutePath());
logger.debug("inputRepresentation :" + inputRepresentation);
fis = new FileInputStream(file);
inputRepresentation = new InputRepresentation(new InputStream() {
private boolean waited = false;
#Override
public int read() throws IOException {
waited = false;
// read the next byte of the FileInputStream, when reaching the
// end of the file, wait for 2 seconds and try again, in case
// the file was not completely created yet
while (true) {
byte[] b = new byte[1];
if (fis.read(b, 0, 1) > 0) {
return b[0] + 256;
} else {
if (waited) {
return -1;
} else {
try {
Thread.sleep(2000);
} catch (InterruptedException ex) {
logger.error("Exception while streaming video : ", ex);
}
waited = true;
}
}
}
}
}, MediaType.VIDEO_MP4);
} else {
logger.debug("Requested file not found : " + filePath);
}
//logger.debug("inputRepresentation :");
return inputRepresentation;
}
Thanks in advance
After reading your comment, here is my understanding of what you should do.
I would not send json to a resource in order to get something, I would just send a simple GET request.
You need:
a resource that returns the file of a video according to its identifier. For the matter of illustration, let's say its url template is /videos/{videoid}
a web page that contains the links, and the empty video player
some javascript that set the "src" attribute video player with the url defined above: /videos/{videoid}. The way you compute the videoid is your own business.
Here is the server code:
the Restlet application, that defines the URI templates
#Override
public Restlet createInboundRoot() {
Router router = new Router(getContext());
// attaches the resource that represents a video, according to its identifier
router.attach("/videos/{videoid}", VideoServerResource.class);
// ... other instructions
return router;
}
the video server resource:
public class VideoServerResource extends ServerResource {
private File video;
#Override
protected void doInit() throws ResourceException {
String videoId = getAttribute("videoid");
// Compute path
String path = "/tmp/" + videoId + ".mp4";
video = new File(path);
// takes care of not found status responses.
setExisting(video.isFile());
}
#Get("mp4")
public File represent() {
return video;
}
}
Here is the client code. This is a sample Web page, with an empty video player. When clicking on the button, the video player is asked to play the http://example.com:9000/videos/testvideo video. In your case, the value testvideo is simply deduced from the link the user click on.
<!DOCTYPE html>
<html>
<head>
<script src="/static/jquery.js"></script>
<script>
$('#playVideo').click(function (){
srcPath = "http://127.0.0.1:9000/videos/testvideo";
$('#videoTab').attr('src', srcPath);
$('#videoTab').css('display', 'block');
$('#videoTab').attr('autoplay', true);
});
</script>
</head>
<body>
<button id="playVideo" class="btn-primary">PlayVideo</button>
<video id="videoTab" height="300" width="500" style="display: none" controls ></video>
</body>
</html>
I hope this will help you.