//========================================================================
//$Id$
//Copyright 2009 Mort Bay Consulting Pty. Ltd.
//------------------------------------------------------------------------
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//http://www.apache.org/licenses/LICENSE-2.0
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
//========================================================================
package org.mortbay.ijetty.console;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.StringTokenizer;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.util.IO;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.media.MediaScannerConnection;
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Config;
import android.util.Log;
/**
* MediaBrowserServlet
*
* /console/browse/media/[type]/[location]/[thumb]/[id][?action=x]
*
* type: image, audio, video
* location: internal, external
* action: embed, upload
*
*
* /console/browse/media/image/internal/3
* Retrieves image 3 from internal storage
*
* /console/browse/media/image/internal/thumb/3
* Retrieves the thumbnail for image 3 from internal storage
*
* /console/browse/media/?action=upload
* Submits a new file to upload to the Android device
*
*
*/
public class MediaBrowserServlet extends HttpServlet
{
public class MyMediaConnectorClient implements MediaScannerConnectionClient
{
private MediaScannerConnection _scanner = null;
private final File _file;
public MyMediaConnectorClient(File file)
{
_file = file;
}
public void onMediaScannerConnected()
{
_scanner.scanFile(_file.getAbsolutePath(),null);
}
public void onScanCompleted(String path, Uri uri)
{
Log.i(TAG,"Finished scanning!");
_scanner.disconnect();
}
public void setScanner(MediaScannerConnection scanner)
{
_scanner = scanner;
}
}
private static final long serialVersionUID = 1L;
private static final String TAG = "MediaBrowserServlet";
private ContentResolver resolver;
private Context context;
@Override
public void init(ServletConfig config) throws ServletException
{
super.init(config);
resolver = (ContentResolver)getServletContext().getAttribute("org.mortbay.ijetty.contentResolver");
context = (Context)getServletContext().getAttribute("org.mortbay.ijetty.context");
}
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
String pathInfo = request.getPathInfo();
if (pathInfo == null)
{
Log.w(TAG,"pathInfo was null, returning 404");
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
if (Config.LOGD)
{
Log.d(TAG,"PathInfo: " + pathInfo);
}
String type = null;
String location = null;
String id = null;
boolean isThumb = false;
StringTokenizer strtok = new StringTokenizer(pathInfo,"/");
if (strtok.hasMoreElements())
{
type = strtok.nextToken();
}
if (strtok.hasMoreElements())
{
location = strtok.nextToken();
}
if (strtok.hasMoreElements())
{
String tmp = strtok.nextToken();
if ("thumb".equalsIgnoreCase(tmp.trim()))
isThumb = true;
else
{
id = removeFileExtension(tmp);
}
}
if (strtok.hasMoreElements())
{
id = removeFileExtension(strtok.nextToken());
}
String action = request.getParameter("action");
if (action !=null && "embed".equals(action.trim()))
{
doGetEmbedHtml(request,response,type,location,id);
}
else
doGetFetchMedia(request, response, type, location, id, isThumb);
}
public String removeFileExtension (String s)
{
if (s == null || "".equals(s))
return s;
int i = s.indexOf('.');
if (i < 0)
return s;
return s.substring(0,i);
}
/**
* Get the content.
*
* @param request
* @param response
* @param contenturi
* @param item
* @param asThumb
* @throws ServletException
* @throws IOException
*/
public void doGetFetchMedia(HttpServletRequest request, HttpServletResponse response, String type, String location, String item, boolean asThumb)
throws ServletException, IOException
{
if (item == null)
{
response.sendError(HttpServletResponse.SC_NOT_FOUND, "No id");
return;
}
try
{
Uri uri = MediaType.getContentUriByType(type, location);
Uri content = Uri.withAppendedPath(uri,item);
if (asThumb)
{
//Get a thumbnail
Cursor c = null;
try
{
c = MediaStore.Images.Thumbnails.queryMiniThumbnail(resolver, Long.parseLong(item), MediaStore.Images.Thumbnails.MINI_KIND, null);
if (c != null && c.moveToFirst())
{
int i = c.getColumnIndex(MediaStore.Images.Thumbnails.DATA);
String s = c.getString(i);
if (s != null)
{
File f = new File(s);
FileInputStream fis = new FileInputStream(f);
response.setStatus(HttpServletResponse.SC_OK);
OutputStream os = response.getOutputStream();
if (resolver.getType(content) == "image/gif")
{
response.setContentType("image/gif");
}
else
{
response.setContentType("image/png");
}
try
{
IO.copy(fis,os);
}
finally
{
fis.close();
}
}
else
response.sendError(HttpServletResponse.SC_NO_CONTENT);
}
}
finally
{
if (c != null)
c.close();
}
}
else
{
Log.i(TAG,"Exporting original media");
//Get the metadata to get the size
long size = -1;
Cursor c = null;
try
{
c = resolver.query(content, new String[] {MediaStore.MediaColumns.SIZE}, null, null, null);
if (c!= null && c.moveToFirst())
size = c.getLong(c.getColumnIndexOrThrow(MediaStore.MediaColumns.SIZE));
}
finally
{
if (c != null)
c.close();
}
InputStream stream = null;
try
{
response.setContentType(resolver.getType(content));
response.setHeader("connection", "close");
if (size > -1)
response.setContentLength((int)size);
stream = resolver.openInputStream(content);
OutputStream os = response.getOutputStream();
response.setStatus(HttpServletResponse.SC_OK);
IO.copy(stream,os);
}
finally
{
if (stream != null)
stream.close();
}
}
}
catch (Exception e)
{
Log.i(TAG,"Failed to fetch media "+ e.getMessage());
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
}
}
/**
* Get the HTML snippet for embedding the media content in a way suitable for the browser to view the content as
*
* @param request
* the incoming servlet request
* @param response
* the outgoing servlet response
* @param type
* the type of media interested in (<code>images</code>, <code>audio</code>, or <code>video</code>)
* @param location
* the location of media interested in (<code>internal</code>, or <code>external</code>)
* @param item
* the item reference
* @throws ServletException
* @throws IOException
*/
public void doGetEmbedHtml(HttpServletRequest request, HttpServletResponse response, String type, String location, String item) throws ServletException,
IOException
{
response.setStatus(HttpServletResponse.SC_OK);
response.setContentType("text/html");
PrintWriter writer = response.getWriter();
String path = "/console/browse/media/" + type + "/" + location + "/" + item;
writer.print("<OBJECT ID='MediaPlayer' WIDTH='320' HEIGHT='26'");
writer.println(" CLASSID='CLSID:22D6F312-B0F6-11D0-94AB-0080C74C7E95'");
writer.println(" STANDBY='Loading...' TYPE='application/x-oleobject'>");
writer.printf(" <PARAM NAME='FileName' VALUE='%s'>%n",path);
writer.printf(" <EMBED TYPE='application/x-mplayer2' SRC='%s' NAME='MediaPlayer' WIDTH='320' HEIGHT='26' autostart='1'></EMBED>%n",path);
writer.println("</OBJECT>");
}
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
String action = request.getParameter("action");
if (action == null || !"upload".equalsIgnoreCase(action))
{
response.sendError(HttpServletResponse.SC_NOT_FOUND, "Unrecognized action");
return;
}
//Upload new media content
response.setContentType("text/html");
response.setStatus(HttpServletResponse.SC_OK);
PrintWriter writer = response.getWriter();
// Should we use just the servlet directory instead?
File externalDir = Environment.getExternalStorageDirectory();
File sdcarddir = new File(externalDir,"/jetty/media");
// Create file upload directory if it doesn't exist
if (!sdcarddir.exists())
sdcarddir.mkdir();
File output = null;
FileOutputStream stream = null;
try
{
// Save file to External Storage
File file = (File)request.getAttribute("fileupload");
String origName = request.getParameter("fileupload");
ByteArrayOutputStream out = new ByteArrayOutputStream();
IO.copy(new FileInputStream(file),out);
output = new File(sdcarddir,origName);
Log.i(TAG,"Writing to: " + output);
stream = new FileOutputStream(output);
out.writeTo(stream);
}
catch (Exception e)
{
Log.w(TAG,"Failed to save uploaded file",e);
printResponse(writer,1,"Could not save uploaded file to sdcard.",-1);
return;
}
finally
{
if (stream != null)
stream.close();
}
// Re-run media scanner, to re-detect media
MyMediaConnectorClient client = new MyMediaConnectorClient(output);
MediaScannerConnection scanner = new MediaScannerConnection(context,client);
client.setScanner(scanner);
scanner.connect();
int filetype = -1;
printResponse(writer,0,"No error",filetype);
}
public ContentResolver getContentResolver()
{
return resolver;
}
private void printResponse(PrintWriter writer, int resp, String msg, int filetype)
{
writer.println("<script>");
writer.println("var json = { error: " + resp + ", msg: '" + msg + "', filetype: " + filetype + " };");
writer.println("if (top.Media) { top.Media.uploadComplete(json); }");
writer.println("</script>");
}
}