Flow.js and Java Servlet upload file 0 bytes - java

I added flow.js in my proyect following the instructions and the call to my java servlet:
localhost:8080/WebExample/UploadImgServlet?flowChunkNumber=1&flowChunkSize=1048576&flowCurrentChunkSize=693916&flowTotalSize=693916&flowIdentifier=693916-image2png&flowFilename=image2.png&flowRelativePath=image2.png&flowTotalChunks=1`
In my servlet I get all parameters of the url (flowChuckNumber, flowChuckSize, etc) but when I try to get the file (request.getInputStream()), it's empty and upload 0 bytes.
Where is the problem? Any Idea?
I found a similar question but it was with PHP...
My code:
HTML(the image is displayed):
...
...
<div flow-init="{singleFile:true}"
flow-file-added="!!{png:1,gif:1,jpg:1,jpeg:1}[$file.getExtension()]"
flow-files-submitted="$flow.upload()"
flow-file-success="$file.msg = $message">
<div class="drop" flow-drop ng-class="dropClass">
<md-button class="md-raised md-primary" type="file" flow-btn>Upload Image</md-button>
<b>OR</b>
Drag And Drop your image here
</div>
<div class="thumbnail" ng-show="!$flow.files.length">
<img src="http://www.placehold.it/200x150/EFEFEF/AAAAAA&text=no+image" alt="Image"/>
</div>
<div class="thumbnail" ng-show="$flow.files.length">
<img flow-img="$flow.files[0]" />
</div>
<table>
<tr ng-repeat="file in $flow.files">
<td>{{$index+1}}</td>
<td>{{file.name}}</td>
<td>{{file.msg}}</td>
</tr>
</table>
</div>
...
...
App AngularJs:
var app = angular.module("webexample", ['ngMaterial', 'ngNotify','uiGmapgoogle-maps','flow'])
.config(['flowFactoryProvider', function (flowFactoryProvider) {
flowFactoryProvider.defaults = {
target: '/WebExample/UploadImgServlet',
permanentErrors: [404, 500, 501],
maxChunkRetries: 1,
chunkRetryInterval: 5000,
simultaneousUploads: 1
};
flowFactoryProvider.on('catchAll', function (event) {
console.log('catchAll', arguments);
});
// Can be used with different implementations of Flow.js
// flowFactoryProvider.factory = fustyFlowFactory;
}])
.directive('appDownloadUrl', [function () {
return {
restrict: 'A',
link: function(scope, element, attrs) {
element.bind('dragstart', function (event) {
var config = scope.$eval(attrs.appDownloadUrl);
if (!config.disabled) {
var data = config.mime + ':' + config.name + ':' + window.location.href + config.url;
console.log("data: "+data);
event.dataTransfer.setData('DownloadURL', data);
}
});
}
};
}])
.directive("appDragstart", [function () {
return function(scope, element, attrs) {
element.bind('dragstart', function (event) {
scope.$eval(attrs.appDragstart);
});
}
}]).directive("appDragend", [function () {
return function(scope, element, attrs) {
element.bind('dragend', function (event) {
scope.$eval(attrs.appDragend);
});
}
}]).run(function ($rootScope) {
$rootScope.dropEnabled = true;
});
My Servlet (I followed this example):
protected void doService(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException
{
LOGGER.debug("[UploadImgServlet - doService] - init");
int resumableChunkNumber = getResumableChunkNumber(request);
ResumableInfo info = getResumableInfo(request);
//info contains all flow parameters of the url.
RandomAccessFile raf = new RandomAccessFile(info.resumableFilePath, "rw");
//Seek to position
raf.seek((resumableChunkNumber - 1) * info.resumableChunkSize);
//Save to file
InputStream is = request.getInputStream();
long readed = 0;
long content_length = request.getContentLength();
//**PROBLEM: request.getContentLength return -1 so read 0 bytes**
byte[] bytes = new byte[1024 * 100];
while(readed < content_length) {
int r = is.read(bytes);
if (r < 0) {
break;
}
raf.write(bytes, 0, r);
readed += r;
}
raf.close();
...
...

The input stream will be empty because flowjs posts the content using MultiPart by default.
The author of the Java code specified that "Octet" should be used for uploads, not multi-part.
UploadServlet accepts Resumable.js Upload with 'octet'
You need to add "method:octet" to your init,
<div flow-init="{singleFile:true, method:octet}"
I am using Spring, so I just used MultipartHttpServletRequest to get the posted data with MultiPart instead because MultiPart is more common.
This is how I received the contents of the file:
Iterator<String> itr = request.getFileNames();
/* Iterate each file, there should only be one/one chunk */
while (itr.hasNext()) {
fileUploaded = request.getFile(itr.next());
raf.write(fileUploaded.getBytes());
}
raf.close();
I had to do more fixes to the java code provided because it was estimating the number of chunks to receive wrong, so I just used the "flowTotalChunks" parameter.

You don't need to worry about content length. The HttpServletRequest will terminate the input stream at the correct point. Just read until end of stream.

I wanted a lib to upload images with more options and visually appealing (drop file from a folder, thumbnail, etc) than the default html input and I have not been able to do with Flowjs and Java Servlet, so I looked for another lib:
https://github.com/danialfarid/ng-file-upload
https://angular-file-upload.appspot.com/
With this lib, I found it easy to use with Java Servlet.
I don't mark this post as solved for if someone finds a way to do it with Flowjs.

Related

How to call <li> id values from the property file using java?

Currently I'm working on Selenium WebDriver and Java. I have a master script which runs all the other scripts. The master script as follows:
import org.openqa.selenium.WebDriver;
public class MasterScript {
public static void main(String[] args) throws Exception {
//*****************************************************
// Calling Methods
//*****************************************************
LoginOneReports utilObj = new LoginOneReports ();// calling my 1st LoginOneReports.java
WebDriver driver;
driver=utilObj.setUp();
if(utilObj.Login()){
System.out.println("Login sucessfully completed");
} else {
System.out.println("Login failed");
System.exit(0);
}
NewPR utilObj1 = new NewPR(driver);// calling my 2st NewPR.java Here I need to change
if(utilObj1.test()){
System.out.println("NewPR KPI page has opened");
} else {
System.out.println("NewPR KPI not able to open");
}
FilterSection utilObj2 =new FilterSection(driver);//calling my 3st Filtersection.java
utilObj2.FilterMatching();
}
}
I have a list of kPI's as follows:
Currently in my NewPR.java script I'm directly going and clicking on the NewPR
The script as follows:
Log.info("Clicking on Overview and Evolution PR link");
if(existsElement("ext-new-prs")==true){
JavascriptExecutor executor = (JavascriptExecutor)driver;
executor.executeScript("arguments[0].click();", driver.findElement(By.id("ext-new-prs") ));
Thread.sleep(6000);
}
else{
Log.info("element not present");
System.out.println("element not present -- so it entered the else loop");
}
return driver.getCurrentUrl().equals("https://10.4.16.159/extranet_newprs/reports/type/default/");
}
Instead of going and clicking each time one KPI. I need to store in a property file where all the list of values in screenshot. Then i need to call any 1 or more KPI and i need to run the 3rd script that is FilterSection.java
My problem is all the KPI HTML are looks like this
<li>
<a id="ext-pr" class="submenu ext-pr" name="ext-pr" href="https://10.4.16.159/reports/">Problem Reports (PR)</a>
<ul>
<li>
<a id="ext-pr-backlog-age" class=" ext-pr-backlog-age" name="ext-pr-backlog-age" href="https://10.4.16.159/extranet_prbacklogage/reports/type/default/">Age</a>
</li>
<li>
<a id="ext-timetoassign-prs" class=" ext-timetoassign-prs" name="ext-timetoassign-prs" href="https://10.4.16.159/extranet_timetoassignprs/reports/type/default/">Average Time To Assign</a>
</li>
I don't have idea how to store these set of values in a property file. could anyone suggest me solution..
Here is a method to load a properties file :
public static Properties loadPropertiesFile(){
Properties properties = new Properties();
InputStream input = null;
try {
input = new FileInputStream("path_to_your_properties_file");
// load a properties file
properties.load(input);
} catch (IOException ex) {
ex.printStackTrace();
} finally {
if (input != null) {
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
If you only provide a file name in path_to_your_properties_file, the file will be located in your project directory. But you can provide a path to a file also I think.
For convenience, use a property file composed of something like that :
total_ids=5
id_1=ext-pr
id_2=ext-pr-backlog-age
id_3=ext-timetoassign-prs
id_4=other_id
id_5=other_id_again
Then after loading your file with the first given method, you can loop through the ids with :
int total = Integer.parseInt(properties.getProperty("total_ids"));
for(int i=1 ; i<= total ; i++){
String identifier = properties.getProperty("id_"+i);
WebElement element = driver.findElement(By.id(identifier));
// Do your tests en 'element'
}
Is it what you want to do ?

How to pass user entered data in a web form and pass it into a Java Script function

I am trying to take the inputed value from here:
index.html
<form name="ytenter" action="youtbe.html" method="get">
url: <input type="text" id="url"name="url">
stop <input type="text" id="stop" name="stop">
<input type="submit" value="Submit" onclick="ytstop(this.stop)">
</form>
and take the entered value in id=url and id=stop
and run it to youtbe.html here:
<head>
<script>
(function ytstop() {
var stopPlayAt=10; // Stop play at time in seconds
var stopPlayTimer; // Reference to settimeout call
// This code loads the IFrame Player API code asynchronously.
var tag = document.createElement("script");
tag.src = "//www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName("script")[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
// This function creates an <iframe> (and YouTube player)
// after the API code downloads.
var player;
window.onYouTubeIframeAPIReady = function () {
player = new YT.Player("player", {
"height": "315",
"width": "560",
"videoId": "L6cVcbkx8l8",
"events": {
"onReady": onPlayerReady,
"onStateChange": onPlayerStateChange
}
});
}
// The API will call this function when the video player is ready.
// This automatically starts the video playback when the player is loaded.
function onPlayerReady(event) {
event.target.playVideo();
}
// The API calls this function when the player's state changes.
function onPlayerStateChange(event) {
var time, rate, remainingTime;
clearTimeout(stopPlayTimer);
if (event.data == YT.PlayerState.PLAYING) {
time = player.getCurrentTime();
// Add .4 of a second to the time in case it's close to the current time
// (The API kept returning ~9.7 when hitting play after stopping at 10s)
if (time + .4 < stopPlayAt) {
rate = player.getPlaybackRate();
remainingTime = (stopPlayAt - time) / rate;
stopPlayTimer = setTimeout(pauseVideo, remainingTime * 1000);
}
}
}
function pauseVideo() {
player.pauseVideo();
}
})();
</script>
</head>
<body>
<div id="player">
</div>
</html>
I need to take the "stop" from the form and put it into stopPlayAt VAR at the top
Then I need to take "url" entered from the form and put it into "videoID":
Thanks for your help. Just want the end product to allow youtube link to be entered and a time to be entered and it runs it until that time entered in seconds.
I would like to refer you to the top answer on this page:
How to get the value from the GET parameters?
This enables you to get the value of the GET-parameters in the URL.

Button on website for taking screenshot [duplicate]

Google's "Report a Bug" or "Feedback Tool" lets you select an area of your browser window to create a screenshot that is submitted with your feedback about a bug.
Screenshot by Jason Small, posted in a duplicate question.
How are they doing this? Google's JavaScript feedback API is loaded from here and their overview of the feedback module will demonstrate the screenshot capability.
JavaScript can read the DOM and render a fairly accurate representation of that using canvas. I have been working on a script which converts HTML into a canvas image. Decided today to make an implementation of it into sending feedbacks like you described.
The script allows you to create feedback forms which include a screenshot, created on the client's browser, along with the form. The screenshot is based on the DOM and as such may not be 100% accurate to the real representation as it does not make an actual screenshot, but builds the screenshot based on the information available on the page.
It does not require any rendering from the server, as the whole image is created on the client's browser. The HTML2Canvas script itself is still in a very experimental state, as it does not parse nearly as much of the CSS3 attributes I would want it to, nor does it have any support to load CORS images even if a proxy was available.
Still quite limited browser compatibility (not because more couldn't be supported, just haven't had time to make it more cross browser supported).
For more information, have a look at the examples here:
http://hertzen.com/experiments/jsfeedback/
edit
The html2canvas script is now available separately here and some examples here.
edit 2
Another confirmation that Google uses a very similar method (in fact, based on the documentation, the only major difference is their async method of traversing/drawing) can be found in this presentation by Elliott Sprehn from the Google Feedback team:
http://www.elliottsprehn.com/preso/fluentconf/
Your web app can now take a 'native' screenshot of the client's entire desktop using getUserMedia():
Have a look at this example:
https://www.webrtc-experiment.com/Pluginfree-Screen-Sharing/
The client will have to be using chrome (for now) and will need to enable screen capture support under chrome://flags.
PoC
As Niklas mentioned you can use the html2canvas library to take a screenshot using JS in the browser. I will extend his answer in this point by providing an example of taking a screenshot using this library ("Proof of Concept"):
function report() {
let region = document.querySelector("body"); // whole screen
html2canvas(region, {
onrendered: function(canvas) {
let pngUrl = canvas.toDataURL(); // png in dataURL format
let img = document.querySelector(".screen");
img.src = pngUrl;
// here you can allow user to set bug-region
// and send it with 'pngUrl' to server
},
});
}
.container {
margin-top: 10px;
border: solid 1px black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js"></script>
<div>Screenshot tester</div>
<button onclick="report()">Take screenshot</button>
<div class="container">
<img width="75%" class="screen">
</div>
In report() function in onrendered after getting image as data URI you can show it to the user and allow him to draw "bug region" by mouse and then send a screenshot and region coordinates to the server.
In this example async/await version was made: with nice makeScreenshot() function.
UPDATE
Simple example which allows you to take screenshot, select region, describe bug and send POST request (here jsfiddle) (the main function is report()).
async function report() {
let screenshot = await makeScreenshot(); // png dataUrl
let img = q(".screen");
img.src = screenshot;
let c = q(".bug-container");
c.classList.remove('hide')
let box = await getBox();
c.classList.add('hide');
send(screenshot,box); // sed post request with bug image, region and description
alert('To see POST requset with image go to: chrome console > network tab');
}
// ----- Helper functions
let q = s => document.querySelector(s); // query selector helper
window.report = report; // bind report be visible in fiddle html
async function makeScreenshot(selector="body")
{
return new Promise((resolve, reject) => {
let node = document.querySelector(selector);
html2canvas(node, { onrendered: (canvas) => {
let pngUrl = canvas.toDataURL();
resolve(pngUrl);
}});
});
}
async function getBox(box) {
return new Promise((resolve, reject) => {
let b = q(".bug");
let r = q(".region");
let scr = q(".screen");
let send = q(".send");
let start=0;
let sx,sy,ex,ey=-1;
r.style.width=0;
r.style.height=0;
let drawBox= () => {
r.style.left = (ex > 0 ? sx : sx+ex ) +'px';
r.style.top = (ey > 0 ? sy : sy+ey) +'px';
r.style.width = Math.abs(ex) +'px';
r.style.height = Math.abs(ey) +'px';
}
//console.log({b,r, scr});
b.addEventListener("click", e=>{
if(start==0) {
sx=e.pageX;
sy=e.pageY;
ex=0;
ey=0;
drawBox();
}
start=(start+1)%3;
});
b.addEventListener("mousemove", e=>{
//console.log(e)
if(start==1) {
ex=e.pageX-sx;
ey=e.pageY-sy
drawBox();
}
});
send.addEventListener("click", e=>{
start=0;
let a=100/75 //zoom out img 75%
resolve({
x:Math.floor(((ex > 0 ? sx : sx+ex )-scr.offsetLeft)*a),
y:Math.floor(((ey > 0 ? sy : sy+ey )-b.offsetTop)*a),
width:Math.floor(Math.abs(ex)*a),
height:Math.floor(Math.abs(ex)*a),
desc: q('.bug-desc').value
});
});
});
}
function send(image,box) {
let formData = new FormData();
let req = new XMLHttpRequest();
formData.append("box", JSON.stringify(box));
formData.append("screenshot", image);
req.open("POST", '/upload/screenshot');
req.send(formData);
}
.bug-container { background: rgb(255,0,0,0.1); margin-top:20px; text-align: center; }
.send { border-radius:5px; padding:10px; background: green; cursor: pointer; }
.region { position: absolute; background: rgba(255,0,0,0.4); }
.example { height: 100px; background: yellow; }
.bug { margin-top: 10px; cursor: crosshair; }
.hide { display: none; }
.screen { pointer-events: none }
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js"></script>
<body>
<div>Screenshot tester</div>
<button onclick="report()">Report bug</button>
<div class="example">Lorem ipsum</div>
<div class="bug-container hide">
<div>Select bug region: click once - move mouse - click again</div>
<div class="bug">
<img width="75%" class="screen" >
<div class="region"></div>
</div>
<div>
<textarea class="bug-desc">Describe bug here...</textarea>
</div>
<div class="send">SEND BUG</div>
</div>
</body>
Get screenshot as Canvas or Jpeg Blob / ArrayBuffer using getDisplayMedia API:
FIX 1: Use the getUserMedia with chromeMediaSource only for Electron.js
FIX 2: Throw error instead return null object
FIX 3: Fix demo to prevent the error: getDisplayMedia must be called from a user gesture handler
// docs: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia
// see: https://www.webrtc-experiment.com/Pluginfree-Screen-Sharing/#20893521368186473
// see: https://github.com/muaz-khan/WebRTC-Experiment/blob/master/Pluginfree-Screen-Sharing/conference.js
function getDisplayMedia(options) {
if (navigator.mediaDevices && navigator.mediaDevices.getDisplayMedia) {
return navigator.mediaDevices.getDisplayMedia(options)
}
if (navigator.getDisplayMedia) {
return navigator.getDisplayMedia(options)
}
if (navigator.webkitGetDisplayMedia) {
return navigator.webkitGetDisplayMedia(options)
}
if (navigator.mozGetDisplayMedia) {
return navigator.mozGetDisplayMedia(options)
}
throw new Error('getDisplayMedia is not defined')
}
function getUserMedia(options) {
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
return navigator.mediaDevices.getUserMedia(options)
}
if (navigator.getUserMedia) {
return navigator.getUserMedia(options)
}
if (navigator.webkitGetUserMedia) {
return navigator.webkitGetUserMedia(options)
}
if (navigator.mozGetUserMedia) {
return navigator.mozGetUserMedia(options)
}
throw new Error('getUserMedia is not defined')
}
async function takeScreenshotStream() {
// see: https://developer.mozilla.org/en-US/docs/Web/API/Window/screen
const width = screen.width * (window.devicePixelRatio || 1)
const height = screen.height * (window.devicePixelRatio || 1)
const errors = []
let stream
try {
stream = await getDisplayMedia({
audio: false,
// see: https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamConstraints/video
video: {
width,
height,
frameRate: 1,
},
})
} catch (ex) {
errors.push(ex)
}
// for electron js
if (navigator.userAgent.indexOf('Electron') >= 0) {
try {
stream = await getUserMedia({
audio: false,
video: {
mandatory: {
chromeMediaSource: 'desktop',
// chromeMediaSourceId: source.id,
minWidth : width,
maxWidth : width,
minHeight : height,
maxHeight : height,
},
},
})
} catch (ex) {
errors.push(ex)
}
}
if (errors.length) {
console.debug(...errors)
if (!stream) {
throw errors[errors.length - 1]
}
}
return stream
}
async function takeScreenshotCanvas() {
const stream = await takeScreenshotStream()
// from: https://stackoverflow.com/a/57665309/5221762
const video = document.createElement('video')
const result = await new Promise((resolve, reject) => {
video.onloadedmetadata = () => {
video.play()
video.pause()
// from: https://github.com/kasprownik/electron-screencapture/blob/master/index.js
const canvas = document.createElement('canvas')
canvas.width = video.videoWidth
canvas.height = video.videoHeight
const context = canvas.getContext('2d')
// see: https://developer.mozilla.org/en-US/docs/Web/API/HTMLVideoElement
context.drawImage(video, 0, 0, video.videoWidth, video.videoHeight)
resolve(canvas)
}
video.srcObject = stream
})
stream.getTracks().forEach(function (track) {
track.stop()
})
if (result == null) {
throw new Error('Cannot take canvas screenshot')
}
return result
}
// from: https://stackoverflow.com/a/46182044/5221762
function getJpegBlob(canvas) {
return new Promise((resolve, reject) => {
// docs: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob
canvas.toBlob(blob => resolve(blob), 'image/jpeg', 0.95)
})
}
async function getJpegBytes(canvas) {
const blob = await getJpegBlob(canvas)
return new Promise((resolve, reject) => {
const fileReader = new FileReader()
fileReader.addEventListener('loadend', function () {
if (this.error) {
reject(this.error)
return
}
resolve(this.result)
})
fileReader.readAsArrayBuffer(blob)
})
}
async function takeScreenshotJpegBlob() {
const canvas = await takeScreenshotCanvas()
return getJpegBlob(canvas)
}
async function takeScreenshotJpegBytes() {
const canvas = await takeScreenshotCanvas()
return getJpegBytes(canvas)
}
function blobToCanvas(blob, maxWidth, maxHeight) {
return new Promise((resolve, reject) => {
const img = new Image()
img.onload = function () {
const canvas = document.createElement('canvas')
const scale = Math.min(
1,
maxWidth ? maxWidth / img.width : 1,
maxHeight ? maxHeight / img.height : 1,
)
canvas.width = img.width * scale
canvas.height = img.height * scale
const ctx = canvas.getContext('2d')
ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height)
resolve(canvas)
}
img.onerror = () => {
reject(new Error('Error load blob to Image'))
}
img.src = URL.createObjectURL(blob)
})
}
DEMO:
document.body.onclick = async () => {
// take the screenshot
var screenshotJpegBlob = await takeScreenshotJpegBlob()
// show preview with max size 300 x 300 px
var previewCanvas = await blobToCanvas(screenshotJpegBlob, 300, 300)
previewCanvas.style.position = 'fixed'
document.body.appendChild(previewCanvas)
// send it to the server
var formdata = new FormData()
formdata.append("screenshot", screenshotJpegBlob)
await fetch('https://your-web-site.com/', {
method: 'POST',
body: formdata,
'Content-Type' : "multipart/form-data",
})
}
// and click on the page
Here is a complete screenshot example that works with chrome in 2021. The end result is a blob ready to be transmitted. Flow is: request media > grab frame > draw to canvas > transfer to blob. If you want to do it more memory efficient explore OffscreenCanvas or possibly ImageBitmapRenderingContext
https://jsfiddle.net/v24hyd3q/1/
// Request media
navigator.mediaDevices.getDisplayMedia().then(stream =>
{
// Grab frame from stream
let track = stream.getVideoTracks()[0];
let capture = new ImageCapture(track);
capture.grabFrame().then(bitmap =>
{
// Stop sharing
track.stop();
// Draw the bitmap to canvas
canvas.width = bitmap.width;
canvas.height = bitmap.height;
canvas.getContext('2d').drawImage(bitmap, 0, 0);
// Grab blob from canvas
canvas.toBlob(blob => {
// Do things with blob here
console.log('output blob:', blob);
});
});
})
.catch(e => console.log(e));
Heres an example using: getDisplayMedia
document.body.innerHTML = '<video style="width: 100%; height: 100%; border: 1px black solid;"/>';
navigator.mediaDevices.getDisplayMedia()
.then( mediaStream => {
const video = document.querySelector('video');
video.srcObject = mediaStream;
video.onloadedmetadata = e => {
video.play();
video.pause();
};
})
.catch( err => console.log(`${err.name}: ${err.message}`));
Also worth checking out is the Screen Capture API docs.
You can try my new JS library: screenshot.js.
It's enable to take real screenshot.
You load the script:
<script src="https://raw.githubusercontent.com/amiad/screenshot.js/master/screenshot.js"></script>
and take screenshot:
new Screenshot({success: img => {
// callback function
myimage = img;
}});
You can read more options in project page.

Direct download from Google Drive using Google Drive API

My desktop application, written in java, tries to download public files from Google Drive. As i found out, it can be implemented by using file's webContentLink (it's for ability to download public files without user authorization).
So, the code below works with small files:
String webContentLink = aFile.getWebContentLink();
InputStream in = new URL(webContentLink).openStream();
But it doesn't work on big files, because in this case file can't be downloaded directly via webContentLink without user confirmation with google virus scan warning. See an example: web content link.
So my question is how to get content of a public file from Google Drive without user authorization?
Update December 8th, 2015
According to Google Support using the
googledrive.com/host/ID
method will be turned off on Aug 31st, 2016.
I just ran into this issue.
The trick is to treat your Google Drive folder like a web host.
Update April 1st, 2015
Google Drive has changed and there's a simple way to direct link to your drive. I left my previous answers below for reference but to here's an updated answer.
Create a Public folder in Google Drive.
Share this drive publicly.
Get your Folder UUID from the address bar when you're in that folder
Put that UUID in this URL
https://googledrive.com/host/<folder UUID>/
Add the file name to where your file is located.
https://googledrive.com/host/<folder UUID>/<file name>
Which is intended functionality by Google
new Google Drive Link.
All you have to do is simple get the host URL for a publicly shared drive folder. To do this, you can upload a plain HTML file and preview it in Google Drive to find your host URL.
Here are the steps:
Create a folder in Google Drive.
Share this drive publicly.
Upload a simple HTML file. Add any additional files (subfolders ok)
Open and "preview" the HTML file in Google Drive
Get the URL address for this folder
Create a direct link URL from your URL folder base
This URL should allow direct downloads of your large files.
[edit]
I forgot to add. If you use subfolders to organize your files, you simple use the folder name as you would expect in a URL hierarchy.
https://googledrive.com/host/<your public folders id string>/images/my-image.png
What I was looking to do
I created a custom Debian image with Virtual Box for Vagrant. I wanted to share this ".box" file with colleagues so they could put the direct link into their Vagrantfile.
In the end, I needed a direct link to the actual file.
Google Drive problem
If you set the file permissions to be publicly available and create/generate a direct access link by using something like the gdocs2direct tool or just crafting the link yourself:
https://docs.google.com/uc?export=download&id=<your file id>
You will get a cookie based verification code and prompt "Google could not scan this file" prompt, which won't work for things such as wget or Vagrantfile configs.
The code that it generates is a simple code that appends GET query variable ...&confirm=### to the string, but it's per user specific, so it's not like you can copy/paste that query variable for others.
But if you use the above "Web page hosting" method, you can get around that prompt.
I hope that helps!
If you face the "This file cannot be checked for viruses" intermezzo page, the download is not that easy.
You essentially need to first download the normal download link, which however redirects you to the "Download anyway" page. You need to store cookies from this first request, find out the link pointed to by the "Download anyway" button, and then use this link to download the file, but reusing the cookies you got from the first request.
Here's a bash variant of the download process using CURL:
curl -c /tmp/cookies "https://drive.google.com/uc?export=download&id=DOCUMENT_ID" > /tmp/intermezzo.html
curl -L -b /tmp/cookies "https://drive.google.com$(cat /tmp/intermezzo.html | grep -Po 'uc-download-link" [^>]* href="\K[^"]*' | sed 's/\&/\&/g')" > FINAL_DOWNLOADED_FILENAME
Notes:
this procedure will probably stop working after some Google changes
the grep command uses Perl syntax (-P) and the \K "operator" which essentially means "do not include anything preceding \K to the matched result. I don't know which version of grep introduced these options, but ancient or non-Ubuntu versions probably don't have it
a Java solution would be more or less the same, just take a HTTPS library which can handle cookies, and some nice text-parsing library
I know this is an old question but I could not find a solution to this problem after some research, so I am sharing what worked for me.
I have written this C# code for one of my projects. It can bypass the scan virus warning programmatically. The code can probably be converted to Java.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Text;
public class FileDownloader : IDisposable
{
private const string GOOGLE_DRIVE_DOMAIN = "drive.google.com";
private const string GOOGLE_DRIVE_DOMAIN2 = "https://drive.google.com";
// In the worst case, it is necessary to send 3 download requests to the Drive address
// 1. an NID cookie is returned instead of a download_warning cookie
// 2. download_warning cookie returned
// 3. the actual file is downloaded
private const int GOOGLE_DRIVE_MAX_DOWNLOAD_ATTEMPT = 3;
public delegate void DownloadProgressChangedEventHandler( object sender, DownloadProgress progress );
// Custom download progress reporting (needed for Google Drive)
public class DownloadProgress
{
public long BytesReceived, TotalBytesToReceive;
public object UserState;
public int ProgressPercentage
{
get
{
if( TotalBytesToReceive > 0L )
return (int) ( ( (double) BytesReceived / TotalBytesToReceive ) * 100 );
return 0;
}
}
}
// Web client that preserves cookies (needed for Google Drive)
private class CookieAwareWebClient : WebClient
{
private class CookieContainer
{
private readonly Dictionary<string, string> cookies = new Dictionary<string, string>();
public string this[Uri address]
{
get
{
string cookie;
if( cookies.TryGetValue( address.Host, out cookie ) )
return cookie;
return null;
}
set
{
cookies[address.Host] = value;
}
}
}
private readonly CookieContainer cookies = new CookieContainer();
public DownloadProgress ContentRangeTarget;
protected override WebRequest GetWebRequest( Uri address )
{
WebRequest request = base.GetWebRequest( address );
if( request is HttpWebRequest )
{
string cookie = cookies[address];
if( cookie != null )
( (HttpWebRequest) request ).Headers.Set( "cookie", cookie );
if( ContentRangeTarget != null )
( (HttpWebRequest) request ).AddRange( 0 );
}
return request;
}
protected override WebResponse GetWebResponse( WebRequest request, IAsyncResult result )
{
return ProcessResponse( base.GetWebResponse( request, result ) );
}
protected override WebResponse GetWebResponse( WebRequest request )
{
return ProcessResponse( base.GetWebResponse( request ) );
}
private WebResponse ProcessResponse( WebResponse response )
{
string[] cookies = response.Headers.GetValues( "Set-Cookie" );
if( cookies != null && cookies.Length > 0 )
{
int length = 0;
for( int i = 0; i < cookies.Length; i++ )
length += cookies[i].Length;
StringBuilder cookie = new StringBuilder( length );
for( int i = 0; i < cookies.Length; i++ )
cookie.Append( cookies[i] );
this.cookies[response.ResponseUri] = cookie.ToString();
}
if( ContentRangeTarget != null )
{
string[] rangeLengthHeader = response.Headers.GetValues( "Content-Range" );
if( rangeLengthHeader != null && rangeLengthHeader.Length > 0 )
{
int splitIndex = rangeLengthHeader[0].LastIndexOf( '/' );
if( splitIndex >= 0 && splitIndex < rangeLengthHeader[0].Length - 1 )
{
long length;
if( long.TryParse( rangeLengthHeader[0].Substring( splitIndex + 1 ), out length ) )
ContentRangeTarget.TotalBytesToReceive = length;
}
}
}
return response;
}
}
private readonly CookieAwareWebClient webClient;
private readonly DownloadProgress downloadProgress;
private Uri downloadAddress;
private string downloadPath;
private bool asyncDownload;
private object userToken;
private bool downloadingDriveFile;
private int driveDownloadAttempt;
public event DownloadProgressChangedEventHandler DownloadProgressChanged;
public event AsyncCompletedEventHandler DownloadFileCompleted;
public FileDownloader()
{
webClient = new CookieAwareWebClient();
webClient.DownloadProgressChanged += DownloadProgressChangedCallback;
webClient.DownloadFileCompleted += DownloadFileCompletedCallback;
downloadProgress = new DownloadProgress();
}
public void DownloadFile( string address, string fileName )
{
DownloadFile( address, fileName, false, null );
}
public void DownloadFileAsync( string address, string fileName, object userToken = null )
{
DownloadFile( address, fileName, true, userToken );
}
private void DownloadFile( string address, string fileName, bool asyncDownload, object userToken )
{
downloadingDriveFile = address.StartsWith( GOOGLE_DRIVE_DOMAIN ) || address.StartsWith( GOOGLE_DRIVE_DOMAIN2 );
if( downloadingDriveFile )
{
address = GetGoogleDriveDownloadAddress( address );
driveDownloadAttempt = 1;
webClient.ContentRangeTarget = downloadProgress;
}
else
webClient.ContentRangeTarget = null;
downloadAddress = new Uri( address );
downloadPath = fileName;
downloadProgress.TotalBytesToReceive = -1L;
downloadProgress.UserState = userToken;
this.asyncDownload = asyncDownload;
this.userToken = userToken;
DownloadFileInternal();
}
private void DownloadFileInternal()
{
if( !asyncDownload )
{
webClient.DownloadFile( downloadAddress, downloadPath );
// This callback isn't triggered for synchronous downloads, manually trigger it
DownloadFileCompletedCallback( webClient, new AsyncCompletedEventArgs( null, false, null ) );
}
else if( userToken == null )
webClient.DownloadFileAsync( downloadAddress, downloadPath );
else
webClient.DownloadFileAsync( downloadAddress, downloadPath, userToken );
}
private void DownloadProgressChangedCallback( object sender, DownloadProgressChangedEventArgs e )
{
if( DownloadProgressChanged != null )
{
downloadProgress.BytesReceived = e.BytesReceived;
if( e.TotalBytesToReceive > 0L )
downloadProgress.TotalBytesToReceive = e.TotalBytesToReceive;
DownloadProgressChanged( this, downloadProgress );
}
}
private void DownloadFileCompletedCallback( object sender, AsyncCompletedEventArgs e )
{
if( !downloadingDriveFile )
{
if( DownloadFileCompleted != null )
DownloadFileCompleted( this, e );
}
else
{
if( driveDownloadAttempt < GOOGLE_DRIVE_MAX_DOWNLOAD_ATTEMPT && !ProcessDriveDownload() )
{
// Try downloading the Drive file again
driveDownloadAttempt++;
DownloadFileInternal();
}
else if( DownloadFileCompleted != null )
DownloadFileCompleted( this, e );
}
}
// Downloading large files from Google Drive prompts a warning screen and requires manual confirmation
// Consider that case and try to confirm the download automatically if warning prompt occurs
// Returns true, if no more download requests are necessary
private bool ProcessDriveDownload()
{
FileInfo downloadedFile = new FileInfo( downloadPath );
if( downloadedFile == null )
return true;
// Confirmation page is around 50KB, shouldn't be larger than 60KB
if( downloadedFile.Length > 60000L )
return true;
// Downloaded file might be the confirmation page, check it
string content;
using( var reader = downloadedFile.OpenText() )
{
// Confirmation page starts with <!DOCTYPE html>, which can be preceeded by a newline
char[] header = new char[20];
int readCount = reader.ReadBlock( header, 0, 20 );
if( readCount < 20 || !( new string( header ).Contains( "<!DOCTYPE html>" ) ) )
return true;
content = reader.ReadToEnd();
}
int linkIndex = content.LastIndexOf( "href=\"/uc?" );
if( linkIndex < 0 )
return true;
linkIndex += 6;
int linkEnd = content.IndexOf( '"', linkIndex );
if( linkEnd < 0 )
return true;
downloadAddress = new Uri( "https://drive.google.com" + content.Substring( linkIndex, linkEnd - linkIndex ).Replace( "&", "&" ) );
return false;
}
// Handles the following formats (links can be preceeded by https://):
// - drive.google.com/open?id=FILEID
// - drive.google.com/file/d/FILEID/view?usp=sharing
// - drive.google.com/uc?id=FILEID&export=download
private string GetGoogleDriveDownloadAddress( string address )
{
int index = address.IndexOf( "id=" );
int closingIndex;
if( index > 0 )
{
index += 3;
closingIndex = address.IndexOf( '&', index );
if( closingIndex < 0 )
closingIndex = address.Length;
}
else
{
index = address.IndexOf( "file/d/" );
if( index < 0 ) // address is not in any of the supported forms
return string.Empty;
index += 7;
closingIndex = address.IndexOf( '/', index );
if( closingIndex < 0 )
{
closingIndex = address.IndexOf( '?', index );
if( closingIndex < 0 )
closingIndex = address.Length;
}
}
return string.Concat( "https://drive.google.com/uc?id=", address.Substring( index, closingIndex - index ), "&export=download" );
}
public void Dispose()
{
webClient.Dispose();
}
}
And here's how you can use it:
// NOTE: FileDownloader is IDisposable!
FileDownloader fileDownloader = new FileDownloader();
// This callback is triggered for DownloadFileAsync only
fileDownloader.DownloadProgressChanged += ( sender, e ) => Console.WriteLine( "Progress changed " + e.BytesReceived + " " + e.TotalBytesToReceive );
// This callback is triggered for both DownloadFile and DownloadFileAsync
fileDownloader.DownloadFileCompleted += ( sender, e ) => Console.WriteLine( "Download completed" );
fileDownloader.DownloadFileAsync( "https://INSERT_DOWNLOAD_LINK_HERE", #"C:\downloadedFile.txt" );
#Case 1: download file with small size.
You can use url with format https://drive.google.com/uc?export=download&id=FILE_ID and then inputstream of file can be obtained directly.
#Case 2: download file with large size.
You stuck a wall of a virus scan alert page returned. By parsing html dom element, I tried to get link with confirm code under button "Download anyway" but it didn't work. Its may required cookie or session info.
enter image description here
SOLUTION:
Finally I found solution for two above cases. Just need to put httpConnection.setDoOutput(true) in connection step to get a Json.
)]}' { "disposition":"SCAN_CLEAN",
"downloadUrl":"http:www...",
"fileName":"exam_list_json.txt", "scanResult":"OK", "sizeBytes":2392}
Then, you can use any Json parser to read downloadUrl, fileName and sizeBytes.
You can refer follow snippet, hope it help.
private InputStream gConnect(String remoteFile) throws IOException{
URL url = new URL(remoteFile);
URLConnection connection = url.openConnection();
if(connection instanceof HttpURLConnection){
HttpURLConnection httpConnection = (HttpURLConnection) connection;
connection.setAllowUserInteraction(false);
httpConnection.setInstanceFollowRedirects(true);
httpConnection.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows 2000)");
httpConnection.setDoOutput(true);
httpConnection.setRequestMethod("GET");
httpConnection.connect();
int reqCode = httpConnection.getResponseCode();
if(reqCode == HttpURLConnection.HTTP_OK){
InputStream is = httpConnection.getInputStream();
Map<String, List<String>> map = httpConnection.getHeaderFields();
List<String> values = map.get("content-type");
if(values != null && !values.isEmpty()){
String type = values.get(0);
if(type.contains("text/html")){
String cookie = httpConnection.getHeaderField("Set-Cookie");
String temp = Constants.getPath(mContext, Constants.PATH_TEMP) + "/temp.html";
if(saveGHtmlFile(is, temp)){
String href = getRealUrl(temp);
if(href != null){
return parseUrl(href, cookie);
}
}
} else if(type.contains("application/json")){
String temp = Constants.getPath(mContext, Constants.PATH_TEMP) + "/temp.txt";
if(saveGJsonFile(is, temp)){
FileDataSet data = JsonReaderHelper.readFileDataset(new File(temp));
if(data.getPath() != null){
return parseUrl(data.getPath());
}
}
}
}
return is;
}
}
return null;
}
And
public static FileDataSet readFileDataset(File file) throws IOException{
FileInputStream is = new FileInputStream(file);
JsonReader reader = new JsonReader(new InputStreamReader(is, "UTF-8"));
reader.beginObject();
FileDataSet rs = new FileDataSet();
while(reader.hasNext()){
String name = reader.nextName();
if(name.equals("downloadUrl")){
rs.setPath(reader.nextString());
} else if(name.equals("fileName")){
rs.setName(reader.nextString());
} else if(name.equals("sizeBytes")){
rs.setSize(reader.nextLong());
} else {
reader.skipValue();
}
}
reader.endObject();
return rs;
}
This seems to be updated again as of May 19, 2015:
How I got it to work:
As in jmbertucci's recently updated answer, make your folder public to everyone. This is a bit more complicated than before, you have to click Advanced to change the folder to "On - Public on the web."
Find your folder UUID as before--just go into the folder and find your UUID in the address bar:
https://drive.google.com/drive/folders/<folder UUID>
Then head to
https://googledrive.com/host/<folder UUID>
It will redirect you to an index type page with a giant subdomain, but you should be able to see the files in your folder. Then you can right click to save the link to the file you want (I noticed that this direct link also has this big subdomain for googledrive.com). Worked great for me with wget.
This also seems to work with others' shared folders.
e.g.,
https://drive.google.com/folderview?id=0B7l10Bj_LprhQnpSRkpGMGV2eE0&usp=sharing
maps to
https://googledrive.com/host/0B7l10Bj_LprhQnpSRkpGMGV2eE0
And a right click can save a direct link to any of those files.
Using a Service Account might work for you.
Check this out:
wget https://raw.githubusercontent.com/circulosmeos/gdown.pl/master/gdown.pl
chmod +x gdown.pl
./gdown.pl https://drive.google.com/file/d/FILE_ID/view TARGET_PATH
Update as of August 2020:
This is what worked for me recently -
Upload your file and get a shareable link which anyone can see(Change permission from "Restricted" to "Anyone with the Link" in the share link options)
Then run:
SHAREABLE_LINK=<google drive shareable link>
curl -L https://drive.google.com/uc\?id\=$(echo $SHAREABLE_LINK | cut -f6 -d"/")
If you just want to programmatically (as oppossed to giving the user a link to open in a browser) download a file through the Google Drive API, I would suggest using the downloadUrl of the file instead of the webContentLink, as documented here: https://developers.google.com/drive/web/manage-downloads
https://github.com/google/skicka
I used this command line tool to download files from Google Drive. Just follow the instructions in Getting Started section and you should download files from Google Drive in minutes.
For any shared link replace FILENAME and FILEID, (for very large files requiring confirmation):
wget --load-cookies /tmp/cookies.txt "https://docs.google.com/uc?export=download&confirm=$(wget --quiet --save-cookies /tmp/cookies.txt --keep-session-cookies --no-check-certificate 'https://docs.google.com/uc?export=download&id=FILEID' -O- | sed -rn 's/.confirm=([0-9A-Za-z_]+)./\1\n/p')&id=FILEID" -O FILENAME && rm -rf /tmp/cookies.txt
(For small files):
wget --no-check-certificate 'https://docs.google.com/uc?export=download&id=FILEID' -O FILENAME
I would consider downloading from the link, scraping the page that you get to grab the confirmation link, and then downloading that.
If you look at the "download anyway" URL it has an extra confirm query parameter with a seemingly randomly generated token. Since it's random...and you probably don't want to figure out how to generate it yourself, scraping might be the easiest way without knowing anything about how the site works.
You may need to consider various scenarios.
I simply create a javascript so that it automatically capture the link and download and close the tab with the help of tampermonkey.
// ==UserScript==
// #name Bypass Google drive virus scan
// #namespace SmartManoj
// #version 0.1
// #description Quickly get the download link
// #author SmartManoj
// #match https://drive.google.com/uc?id=*&export=download*
// #grant none
// ==/UserScript==
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function demo() {
await sleep(5000);
window.close();
}
(function() {
location.replace(document.getElementById("uc-download-link").href);
demo();
})();
Similarly you can get the html source of the url and download in java.
I faced an issue in direct download because I was logged in using multiple Google accounts.
Solution is append authUser=0 parameter. Sample request URL to download :https://drive.google.com/uc?id=FILEID&authuser=0&export=download
https://drive.google.com/uc?export=download&id=FILE_ID replace the FILE_ID with file id.
if you don't know were is file id then check this article Article LINK

WEBSOCKETS: client does not revice the messages I send from server

I will try to explain my problem without attaching any code, I think that that is not needed.
Okay, I have a websocket client in JS that connects to my java server. The handshake is done, connected handler is called on the client, so I send a message to server, wich is readed. Then the message is reversed and sended back to the client, but the client messagerecived handler or any other handler are not called.
This is the message that I send to the client:
b[0]=-127;//Its the same of 129?
b[1]=1;
b[2]=18;//any char..
I think that the problem must be on the first byte. I write "b[0] = (byte)129;" but when I read it it returns -127, maybe because, ¿the byte 129 have to be unsigned?
Thanks for help :P
The requested client code:
<html><head><meta charset="utf-8">
<title>WebSocket Test</title>
<script language="javascript" type="text/javascript">
var wsUri = "ws://localhost:10637/penise";
var output;
function init()
{
output = document.getElementById("output");
testWebSocket();
}
function testWebSocket()
{
websocket = new WebSocket(wsUri);
websocket.onopen = function(evt) { onOpen(evt) };
websocket.onclose = function(evt) { onClose(evt) };
websocket.onmessage = function(evt) { onMessage(evt) };
websocket.onerror = function(evt) { onError(evt) };
}
function onOpen(evt)
{
writeToScreen("CONNECTED");
var msg = String.fromCharCode(1)+ String.fromCharCode(0)+"This is niceeee"
doSend(msg);
}
function onClose(evt)
{
writeToScreen("DISCONNECTED");
}
function onMessage(evt)
{
writeToScreen('<span style="color: blue;">RESPONSE: ' + evt.data+'</span>');
websocket.close();
}
function onError(evt)
{
writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);
}
function doSend(message)
{
writeToScreen("SENT: " + message);
websocket.send(message);
}
function writeToScreen(message)
{
var pre = document.createElement("p");
pre.style.wordWrap = "break-word";
pre.innerHTML = message;
output.appendChild(pre);
}
window.addEventListener("load", init, false);
function char(i) {
return String.fromCharCode(i);
}
</script>
</head><body><h2>WebSocket Test</h2>
<div id="output"><p style="word-wrap: break-word;">CONNECTED</p><p style="word-wrap: break-word;">SENT: This is niceeee</p><p style="word-wrap: break-word;"><span style="color: red;">ERROR:</span> undefined</p><p style="word-wrap: break-word;">DISCONNECTED</p></div>
</body></html>
I would recommend that you use a buffer of unsigned bytes to avoid confusion.
Your main problem is that the first byte indicates that the payload is UTF-8 text, but the single byte value in your payload is 180 which is not a valid UTF-8 character. If you are trying to send a binary value then you need to indicate that in the first byte by setting the opcode to 0x2 rather than 0x1 (e.g. 130 rather than 129).
OK, I have solved the problem. The problem was that, when i sent the header, I putted 2 "\r\n", instead of only one, so one of the \r\n becomes part of the next frame and the connection is broken (because it becomes the opcode of the next frame).
Thanks you for trying to help me <3

Categories