I am using Gralis 1.3.7. I am writing a controller that needs to get a PDF file from another server and return it to the client. I would like to do this in some reasonably efficient manner, such as the following:
class DocController {
def view = {
URL source = new URL("http://server.com?docid=${params.docid}");
response.contentType = 'application/pdf';
// Something like this to set the content length
response.setHeader("Content-Length", source.contentLength.toString());
response << source.openStream();
}
}
The problem I am having is figuring out how to set the content length of the response of my controller based on the information coming back from source. I wasn't able to find the documentation on the URL class as enhanced by grails.
What's the best way to proceed?
Gene
EDITED: Fixed parameter values in setHeader
UPDATED 16 mar 2012 10:49 PST
UPDATED 19 March 2012 10:45 PST
Moved follow-up to a separate question.
You can use java.net.URLConnection object that will allow you to do a bit more detailed work with the URL.
URLConnection connection = new URL(url).openConnection()
def url = new URL("http://www.aboutgroovy.com")
def connection = url.openConnection()
println connection.responseCode // ===> 200
println connection.responseMessage // ===> OK
println connection.contentLength // ===> 4216
println connection.contentType // ===> text/html
println connection.date // ===> 1191250061000
println connection.lastModified
// print headers
connection.headerFields.each{println it}
Your example should looks something like this:
class DocController {
def view = {
URL source = new URL("http://server.com?docid=${params.docid}");
URLConnection connection = source.openConnection();
response.contentType = 'application/pdf';
// Set the content length
response.setHeader("Content-Length", connection.contentLength.toString());
// Get the input stream from the connection
response.outputStream << connection.getInputStream();
}
}
Related
As the title suggests, WLP won't run the process- it won't return anything to the process input stream nor to error stream.
If anyone knows about a configuration that needs to take place I would love to know..
(note the process Can run by running the command manually - in addition, the whole thing runs smooth on tomcat8 so..)
EDIT 1:
The problem was not the command execution under WLP as you guys stated, so I accepted the answer.
The problem is different : I sent a media file to a multipart servlet and stored it in a file on disk using the following code:
InputStream is = request.getInputStream();
String currentTime = new Long(System.currentTimeMillis()).toString();
String fileName = PATH + currentTime + "." + fileType;
File file = new File(fileName);
// write the image to a temporary location
FileOutputStream os = new FileOutputStream(file);
byte[] buffer = new byte[BUFFER_SIZE];
while(true) {
int numRead = is.read(buffer);
if(numRead == -1) {
break;
}
os.write(buffer, 0, numRead);
os.flush();
}
is.close();
os.close();
and the file gets saved along with the following prefix:
While this does not happen on tomcat8 (using the same client)..
something is not trivial in the received input stream. (Note its a multipart servlet that set up via #MultipartConfig only)
Hope this post will help others..
guys,thanks for your help!
This will work in Liberty. I was able to test out the following code in a servlet and it printed the path of my current directory just fine:
String line;
Process p = Runtime.getRuntime().exec("cmd /c cd");
BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
while ((line = input.readLine()) != null) {
System.out.println(line);
}
input.close();
Start with a simple command like this, and when you move up to more complex commands or scripts, make sure you are not burying exceptions that may come back. Always at least print the stack trace!
I'm trying to write a Groovy script that will post a Word (docx) file to a REST handler on my grails application.
The request is constructed like so:
import org.apache.http.HttpEntity
import org.apache.http.HttpResponse
import org.apache.http.client.methods.HttpPost
import org.apache.http.entity.mime.MultipartEntity
import org.apache.http.entity.mime.content.FileBody
import org.apache.http.entity.mime.content.StringBody
import org.apache.http.impl.client.DefaultHttpClient
class RestFileUploader {
def sendFile(file, filename) {
def url = 'http://url.of.my.app';
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(url);
MultipartEntity reqEntity = new MultipartEntity();
FileBody bin = new FileBody(file);
reqEntity.addPart("file", new FileBody((File)file, "application/msword"));
def normalizedFilename = filename.replace(" ", "")
reqEntity.addPart("fileName", new StringBody(normalizedFilename));
httppost.setEntity(reqEntity);
httppost.setHeader('X-File-Size', (String)file.size())
httppost.setHeader('X-File-Name', filename)
httppost.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document; charset=utf-8')
println "about to post..."
HttpResponse restResponse = httpclient.execute(httppost);
HttpEntity resEntity = restResponse.getEntity();
def responseXml = resEntity.content.text;
println "posted..."
println restResponse
println resEntity
println responseXml.toString()
return responseXml.toString()
}
}
On the receiving controller, I read in the needed headers from the request, and then try to access the file like so:
def inStream = request.getInputStream()
I end up writing out a corrupted Word file, and from examining the file size and the contents, it looks like my controller is writing out the entire request, rather than just the file.
I've also tried this approach:
def filePart = request.getPart('file')
def inStream = filePart.getInputStream()
In this case I end up with an empty input stream and nothing gets written out.
I feel like I'm missing something simple here. What am I doing wrong?
You will need to make two changes:
Remove the line: httppost.setHeader('Content-Type'.... File upload HTTP POST requests must have content type multipart/form-data (set automatically by HttpClient when you construct a multipart HttpPost)
Change the line: reqEntity.addPart("file", ... to: reqEntity.addPart("file", new
FileBody(file)). Or use one of the other non-deprecated FileBody constructors to specify a valid content type and charset (API link) This assumes that your file method parameter is of type java.io.File -- this isn't clear to me from your snippet.
Then, as dmahapatro suggests, you should be able to read the file with: request.getFile('file')
I need to extract uploads from http-trafic. How could do that? First of all, the request-method will be POST. Secondly, there will be a Content-Type header-field. I do not want to extract formular-data, but uploads like mail-attachements.
The content type is per specification multipart/form-data.
This is a special content type which can be visualized as multiple sub-requests in one big request. Each of those sub-requests (one form-data element) has their own set of headers. The content type of the actual data is in there.
Here's an example how it look like with 1 normal field and 1 file field (in HTML terms, when using <input name="textfield"><input type="file" name="filefield">):
Content-Type: multipart/form-data;boundary=SOME_BOUNDARY
--SOME_BOUNDARY
content-disposition: form-data;name="textfield"
content-type: text/plain;charset=UTF-8
value of textfield here
--SOME_BOUNDARY
content-disposition: form-data;name="filefield";filename="some.ext"
content-type: application/octet-stream
binary file content here
--SOME_BOUNDARY--
As to parsing and extracting this data, practically every programming language has builtin/3rd party APIs for this. As you didn't tell anything about which one you're using, it's impossible to give a targeted answer. In case of for example Java, that would be either the 3rd party library Apache Commons FileUpload or when you're using Servlet 3.0, the API-provided request.getPart() method.
If (and I by no means am saying this is the correct way) you just want to save data from a byte array, you should look at how to read the POST body at:
Reading POST body with bottle.py
Reading the data and then creating a new file should do the trick.
Based on #BalusC s solution I made a little extension method for .NET's build in WebClient class which does not support Multipart upload out of the box.
Usage
Just mix string values and files (enclosed in #)
public void UploadMultipart()
{
var fileName = "/some/existing/file.ext";
using (var client = new WebClient())
{
var values = new NameValueCollection();
values.Add("id", Guid.NewGuid().ToString());
values.Add("name", Path.GetFileNameWithoutExtension(fileName));
values.Add("file", $"#{fileName}#");
var result = client.UploadMultipart(address, method, values);
var content = client.Encoding.GetString(result);
}
}
Extension method
public static byte[] UploadMultipart(this WebClient client,
string address, string method, NameValueCollection values)
{
string boundary = DateTime.Now.Ticks.ToString("x");
client.Headers.Add("Content-Type", "multipart/form-data; boundary=" + boundary);
var sb = new StringBuilder()
.AppendLine();
foreach (var key in values.AllKeys)
{
var contentDispositon = $"form-data;name=\"{key}\"";
var contentType = $"text/plain;charset={client.Encoding.WebName}";
var value = values[key];
if (value.StartsWith("#") && value.EndsWith("#"))
{
// if a value is enclosed in hashes we expect this to be a path to a file
// file=#/path/to/file.ext#
var fileName = value.Trim('#');
var file = File.ReadAllBytes(fileName);
value = client.Encoding.GetString(file);
contentType = "application/octet-stream";
contentDispositon = $"form-data;name=\"{key}\"filename=\"{Path.GetFileName(fileName)}\"";
}
sb.AppendLine($"--{boundary}")
.AppendLine($"Content-Disposition: {contentDispositon}")
.AppendLine($"Content-Type: {contentType}")
.AppendLine()
.AppendLine(value);
}
sb.AppendLine($"--{boundary}--");
var data = client.Encoding.GetBytes(sb.ToString());
return client.UploadData(address, method, data);
}
I'm using GWT RPC to communicate between client and server.
I want to be able to read the browser's date on the server side, and for that I'm using setRpcRequestBuilder from the class ServiceDefTarget to costumize my request, and add the header I want.
On the client side I'm using:
private static final RpcRequestBuilder rpcReqBuilder = new RpcRequestBuilder() {
#Override
protected RequestBuilder doCreate(String serviceEntryPoint) {
RequestBuilder builder = super.doCreate(serviceEntryPoint);
builder.setHeader("Date1", new Date().toString());
return builder;
}
};
......
((ServiceDefTarget) greetingService).setRpcRequestBuilder(rpcReqBuilder);
//rpc call
greetingService.greetServer(.........)
On the server side I do:
HttpServletRequest request = this.getThreadLocalRequest();
Enumeration<?> enumeration = request.getHeaderNames();
while (enumeration.hasMoreElements()) {
String name = (String) enumeration.nextElement();
String value = request.getHeader(name);
System.out.println(name + ": " + value);
}
which among all the default headers prints
Date1: Tue Apr 10 12:19:28 BST 2012
Ok, this works fine, but when I try to set the "Date" header, then it doesn't show up on the server side. Why is that? Anybody can help. I'll be very helpfull. :)
Date is a predefined header of HTTP, and by definition, XMLHttpRequest (the thing behind GWT's RequestBuilder) cannot let you set it to an arbitrary value.
Anyway, when crafting your own headers, you should add a prefix to avoid conflicts with other things on the network adding headers, something like MyApp-Date or X-MyApp-Date (like GWT does it with X-GWT-Permutation and X-GWT-Module-Base in GWT-RPC and RequestFactory)
I need to load an external web (not local) page into my site (some link), but only a part of it.
What are the options for doing so?
That depends on whether or not the external page is local, or on a different domain. If it's local, you can use $.load() in the jQuery library. This has an optional parameter to specify which element in the remote-dom to load it:
$("#links").load("/Main_Page #jq-p-Getting-Started li");
If the page is on another domain, you'll need a proxy script. You can do this with PHP and the phpQuery (php port of jQuery) library. You'll just use file_get_contents() to get the actual remote-dom, and then pull out the elements you want based on jQuery-like selectors.
$f = fopen('http://www.quran.az/2/255', 'r');
and so on...
Once you get the whole page as Michael Todd outlined, you will likely need to either use substring methods for a static means to slice up the content or you can use regex's for a more dynamic way to grab the content. An intro article on Regex's in ASP.Net can be found here. Good luck!
To load a web page in .Net, use the HttpWebRequest class.
Example taken from MSDN, here:
private string StringGetWebPage(String uri)
{
const int bufSizeMax = 65536; // max read buffer size conserves memory
const int bufSizeMin = 8192; // min size prevents numerous small reads
StringBuilder sb;
// A WebException is thrown if HTTP request fails
try
{
// Create an HttpWebRequest using WebRequest.Create (see .NET docs)!
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
// Execute the request and obtain the response stream
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream responseStream = response.GetResponseStream();
// Content-Length header is not trustable, but makes a good hint.
// Responses longer than int size will throw an exception here!
int length = (int)response.ContentLength;
// Use Content-Length if between bufSizeMax and bufSizeMin
int bufSize = bufSizeMin;
if (length > bufSize)
bufSize = length > bufSizeMax ? bufSizeMax : length;
// Allocate buffer and StringBuilder for reading response
byte[] buf = new byte[bufSize];
sb = new StringBuilder(bufSize);
// Read response stream until end
while ((length = responseStream.Read(buf, 0, buf.Length)) != 0)
sb.Append(Encoding.UTF8.GetString(buf, 0, length));
}
catch (Exception ex)
{
sb = new StringBuilder(ex.Message);
}
return sb.ToString();
}
Note that this will return the entire page and not just a portion of it. You'll then need to sift through the page to find the information you're looking for.