continue integration new model (better security)
This commit is contained in:
parent
c6472630ca
commit
7f7b73ec51
@ -26,7 +26,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>kangaroo-and-rabbit</groupId>
|
<groupId>kangaroo-and-rabbit</groupId>
|
||||||
<artifactId>archidata</artifactId>
|
<artifactId>archidata</artifactId>
|
||||||
<version>0.1.2</version>
|
<version>0.1.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@ import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
|
|||||||
import org.glassfish.jersey.media.multipart.MultiPartFeature;
|
import org.glassfish.jersey.media.multipart.MultiPartFeature;
|
||||||
import org.glassfish.jersey.server.ResourceConfig;
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
import org.kar.karideo.api.*;
|
import org.kar.karideo.api.*;
|
||||||
import org.kar.karideo.model.Data;
|
|
||||||
import org.kar.karideo.model.Media;
|
import org.kar.karideo.model.Media;
|
||||||
import org.kar.karideo.model.Season;
|
import org.kar.karideo.model.Season;
|
||||||
import org.kar.karideo.model.Series;
|
import org.kar.karideo.model.Series;
|
||||||
@ -14,13 +13,14 @@ import org.kar.karideo.model.Type;
|
|||||||
import org.kar.archidata.GlobalConfiguration;
|
import org.kar.archidata.GlobalConfiguration;
|
||||||
import org.kar.archidata.SqlWrapper;
|
import org.kar.archidata.SqlWrapper;
|
||||||
import org.kar.archidata.UpdateJwtPublicKey;
|
import org.kar.archidata.UpdateJwtPublicKey;
|
||||||
|
import org.kar.archidata.api.DataResource;
|
||||||
import org.kar.archidata.db.DBConfig;
|
import org.kar.archidata.db.DBConfig;
|
||||||
import org.kar.archidata.filter.AuthenticationFilter;
|
import org.kar.archidata.filter.AuthenticationFilter;
|
||||||
import org.kar.archidata.filter.CORSFilter;
|
import org.kar.archidata.filter.CORSFilter;
|
||||||
import org.kar.archidata.filter.OptionFilter;
|
import org.kar.archidata.filter.OptionFilter;
|
||||||
|
import org.kar.archidata.model.Data;
|
||||||
import org.kar.archidata.model.User;
|
import org.kar.archidata.model.User;
|
||||||
import org.kar.archidata.util.ConfigBaseVariable;
|
import org.kar.archidata.util.ConfigBaseVariable;
|
||||||
import org.kar.archidata.util.JWTWrapper;
|
|
||||||
import org.glassfish.jersey.jackson.JacksonFeature;
|
import org.glassfish.jersey.jackson.JacksonFeature;
|
||||||
|
|
||||||
import javax.ws.rs.core.UriBuilder;
|
import javax.ws.rs.core.UriBuilder;
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
package org.kar.karideo.annotation;
|
|
||||||
|
|
||||||
import javax.ws.rs.NameBinding;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
import static java.lang.annotation.ElementType.METHOD;
|
|
||||||
import static java.lang.annotation.ElementType.TYPE;
|
|
||||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
|
||||||
|
|
||||||
@NameBinding
|
|
||||||
@Retention(RUNTIME)
|
|
||||||
@Target({METHOD})
|
|
||||||
public @interface PermitTokenInURI {
|
|
||||||
}
|
|
@ -1,426 +0,0 @@
|
|||||||
package org.kar.karideo.api;
|
|
||||||
|
|
||||||
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
|
|
||||||
import org.glassfish.jersey.media.multipart.FormDataParam;
|
|
||||||
import org.kar.karideo.annotation.PermitTokenInURI;
|
|
||||||
import org.kar.archidata.filter.GenericContext;
|
|
||||||
import org.kar.karideo.model.Data;
|
|
||||||
import org.kar.archidata.SqlWrapper;
|
|
||||||
import org.kar.archidata.annotation.security.RolesAllowed;
|
|
||||||
import org.kar.archidata.util.ConfigBaseVariable;
|
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
|
||||||
import javax.ws.rs.*;
|
|
||||||
import javax.ws.rs.core.CacheControl;
|
|
||||||
import javax.ws.rs.core.Context;
|
|
||||||
import javax.ws.rs.core.HttpHeaders;
|
|
||||||
import javax.ws.rs.core.MediaType;
|
|
||||||
import javax.ws.rs.core.Response;
|
|
||||||
import javax.ws.rs.core.SecurityContext;
|
|
||||||
import javax.ws.rs.core.StreamingOutput;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.image.BufferedImage;
|
|
||||||
import java.io.*;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.nio.file.StandardCopyOption;
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
|
|
||||||
// https://stackoverflow.com/questions/35367113/jersey-webservice-scalable-approach-to-download-file-and-reply-to-client
|
|
||||||
// https://gist.github.com/aitoroses/4f7a2b197b732a6a691d
|
|
||||||
|
|
||||||
@Path("/data")
|
|
||||||
@Produces({MediaType.APPLICATION_JSON})
|
|
||||||
public class DataResource {
|
|
||||||
private final static int CHUNK_SIZE = 1024 * 1024; // 1MB chunks
|
|
||||||
private final static int CHUNK_SIZE_IN = 50 * 1024 * 1024; // 1MB chunks
|
|
||||||
/**
|
|
||||||
* Upload some datas
|
|
||||||
*/
|
|
||||||
private static long tmpFolderId = 1;
|
|
||||||
|
|
||||||
private static void createFolder(String path) throws IOException {
|
|
||||||
if (!Files.exists(java.nio.file.Path.of(path))) {
|
|
||||||
//Log.print("Create folder: " + path);
|
|
||||||
Files.createDirectories(java.nio.file.Path.of(path));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long getTmpDataId() {
|
|
||||||
return tmpFolderId++;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getTmpFileInData(long tmpFolderId) {
|
|
||||||
String filePath = ConfigBaseVariable.getTmpDataFolder() + File.separator + tmpFolderId;
|
|
||||||
try {
|
|
||||||
createFolder(ConfigBaseVariable.getTmpDataFolder() + File.separator);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return filePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getFileData(long tmpFolderId) {
|
|
||||||
String filePath = ConfigBaseVariable.getMediaDataFolder() + File.separator + tmpFolderId + File.separator + "data";
|
|
||||||
try {
|
|
||||||
createFolder(ConfigBaseVariable.getMediaDataFolder() + File.separator + tmpFolderId + File.separator);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return filePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Data getWithSha512(String sha512) {
|
|
||||||
System.out.println("find sha512 = " + sha512);
|
|
||||||
try {
|
|
||||||
return SqlWrapper.getWhere(Data.class, "sha512", "=", sha512);
|
|
||||||
} catch (Exception e) {
|
|
||||||
// TODO Auto-generated catch block
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Data getWithId(long id) {
|
|
||||||
System.out.println("find id = " + id);
|
|
||||||
try {
|
|
||||||
return SqlWrapper.get(Data.class, id);
|
|
||||||
} catch (Exception e) {
|
|
||||||
// TODO Auto-generated catch block
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Data createNewData(long tmpUID, String originalFileName, String sha512) throws IOException {
|
|
||||||
// determine mime type:
|
|
||||||
Data injectedData = new Data();
|
|
||||||
String mimeType = "";
|
|
||||||
String extension = originalFileName.substring(originalFileName.lastIndexOf('.') + 1);
|
|
||||||
switch (extension.toLowerCase()) {
|
|
||||||
case "jpg":
|
|
||||||
case "jpeg":
|
|
||||||
mimeType = "image/jpeg";
|
|
||||||
break;
|
|
||||||
case "png":
|
|
||||||
mimeType = "image/png";
|
|
||||||
break;
|
|
||||||
case "webp":
|
|
||||||
mimeType = "image/webp";
|
|
||||||
break;
|
|
||||||
case "mka":
|
|
||||||
mimeType = "audio/x-matroska";
|
|
||||||
break;
|
|
||||||
case "mkv":
|
|
||||||
mimeType = "video/x-matroska";
|
|
||||||
break;
|
|
||||||
case "webm":
|
|
||||||
mimeType = "video/webm";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IOException("Can not find the mime type of data input: '" + extension + "'");
|
|
||||||
}
|
|
||||||
injectedData.mimeType = mimeType;
|
|
||||||
injectedData.sha512 = sha512;
|
|
||||||
String tmpPath = getTmpFileInData(tmpUID);
|
|
||||||
injectedData.size = Files.size(Paths.get(tmpPath));
|
|
||||||
|
|
||||||
try {
|
|
||||||
injectedData = SqlWrapper.insert(injectedData);
|
|
||||||
} catch (Exception e) {
|
|
||||||
// TODO Auto-generated catch block
|
|
||||||
e.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
String mediaPath = getFileData(injectedData.id);
|
|
||||||
System.out.println("src = " + tmpPath);
|
|
||||||
System.out.println("dst = " + mediaPath);
|
|
||||||
Files.move(Paths.get(tmpPath), Paths.get(mediaPath), StandardCopyOption.ATOMIC_MOVE);
|
|
||||||
System.out.println("Move done");
|
|
||||||
return injectedData;
|
|
||||||
}
|
|
||||||
|
|
||||||
static String saveTemporaryFile(InputStream uploadedInputStream, long idData) {
|
|
||||||
return saveFile(uploadedInputStream, DataResource.getTmpFileInData(idData));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void removeTemporaryFile(long idData) {
|
|
||||||
String filepath = DataResource.getTmpFileInData(idData);
|
|
||||||
if (Files.exists(Paths.get(filepath))) {
|
|
||||||
try {
|
|
||||||
Files.delete(Paths.get(filepath));
|
|
||||||
} catch (IOException e) {
|
|
||||||
System.out.println("can not delete temporary file : " + Paths.get(filepath));
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// save uploaded file to a defined location on the server
|
|
||||||
static String saveFile(InputStream uploadedInputStream, String serverLocation) {
|
|
||||||
String out = "";
|
|
||||||
try {
|
|
||||||
OutputStream outpuStream = new FileOutputStream(new File(
|
|
||||||
serverLocation));
|
|
||||||
int read = 0;
|
|
||||||
byte[] bytes = new byte[CHUNK_SIZE_IN];
|
|
||||||
MessageDigest md = MessageDigest.getInstance("SHA-512");
|
|
||||||
|
|
||||||
outpuStream = new FileOutputStream(new File(serverLocation));
|
|
||||||
while ((read = uploadedInputStream.read(bytes)) != -1) {
|
|
||||||
//System.out.println("write " + read);
|
|
||||||
md.update(bytes, 0, read);
|
|
||||||
outpuStream.write(bytes, 0, read);
|
|
||||||
}
|
|
||||||
System.out.println("Flush input stream ... " + serverLocation);
|
|
||||||
System.out.flush();
|
|
||||||
outpuStream.flush();
|
|
||||||
outpuStream.close();
|
|
||||||
// create the end of sha512
|
|
||||||
byte[] sha512Digest = md.digest();
|
|
||||||
// convert in hexadecimal
|
|
||||||
out = bytesToHex(sha512Digest);
|
|
||||||
uploadedInputStream.close();
|
|
||||||
} catch (IOException ex) {
|
|
||||||
System.out.println("Can not write in temporary file ... ");
|
|
||||||
ex.printStackTrace();
|
|
||||||
} catch (NoSuchAlgorithmException ex) {
|
|
||||||
System.out.println("Can not find sha512 algorithms");
|
|
||||||
ex.printStackTrace();
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String bytesToHex(byte[] bytes) {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
for (byte b : bytes) {
|
|
||||||
sb.append(String.format("%02x", b));
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public Data getSmall(Long id) {
|
|
||||||
try {
|
|
||||||
return SqlWrapper.get(Data.class, id);
|
|
||||||
} catch (Exception e) {
|
|
||||||
// TODO Auto-generated catch block
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@POST
|
|
||||||
@Path("/upload/")
|
|
||||||
@Consumes({MediaType.MULTIPART_FORM_DATA})
|
|
||||||
@RolesAllowed("ADMIN")
|
|
||||||
public Response uploadFile(@Context SecurityContext sc, @FormDataParam("file") InputStream fileInputStream, @FormDataParam("file") FormDataContentDisposition fileMetaData) {
|
|
||||||
GenericContext gc = (GenericContext) sc.getUserPrincipal();
|
|
||||||
System.out.println("===================================================");
|
|
||||||
System.out.println("== DATA uploadFile " + (gc==null?"null":gc.user));
|
|
||||||
System.out.println("===================================================");
|
|
||||||
//public NodeSmall uploadFile(final FormDataMultiPart form) {
|
|
||||||
System.out.println("Upload file: ");
|
|
||||||
String filePath = ConfigBaseVariable.getTmpDataFolder() + File.separator + tmpFolderId++;
|
|
||||||
try {
|
|
||||||
createFolder(ConfigBaseVariable.getTmpDataFolder() + File.separator);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
saveFile(fileInputStream, filePath);
|
|
||||||
return Response.ok("Data uploaded successfully !!").build();
|
|
||||||
//return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("{id}")
|
|
||||||
@PermitTokenInURI
|
|
||||||
@RolesAllowed("USER")
|
|
||||||
@Produces(MediaType.APPLICATION_OCTET_STREAM)
|
|
||||||
public Response retriveDataId(@Context SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) String token, @HeaderParam("Range") String range, @PathParam("id") Long id) throws Exception {
|
|
||||||
GenericContext gc = (GenericContext) sc.getUserPrincipal();
|
|
||||||
//System.out.println("===================================================");
|
|
||||||
System.out.println("== DATA retriveDataId ? id=" + id + " user=" + (gc==null?"null":gc.user));
|
|
||||||
//System.out.println("===================================================");
|
|
||||||
Data value = getSmall(id);
|
|
||||||
if (value == null) {
|
|
||||||
Response.status(404).
|
|
||||||
entity("media NOT FOUND: " + id).
|
|
||||||
type("text/plain").
|
|
||||||
build();
|
|
||||||
}
|
|
||||||
return buildStream(ConfigBaseVariable.getMediaDataFolder() + File.separator + id + File.separator + "data", range, value.mimeType);
|
|
||||||
}
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("thumbnail/{id}")
|
|
||||||
@RolesAllowed("USER")
|
|
||||||
@PermitTokenInURI
|
|
||||||
@Produces(MediaType.APPLICATION_OCTET_STREAM)
|
|
||||||
//@CacheMaxAge(time = 10, unit = TimeUnit.DAYS)
|
|
||||||
public Response retriveDataThumbnailId(@Context SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) String token, @HeaderParam("Range") String range, @PathParam("id") Long id) throws Exception {
|
|
||||||
GenericContext gc = (GenericContext) sc.getUserPrincipal();
|
|
||||||
//System.out.println("===================================================");
|
|
||||||
//System.out.println("== DATA retriveDataThumbnailId ? " + (gc==null?"null":gc.user));
|
|
||||||
//System.out.println("===================================================");
|
|
||||||
Data value = getSmall(id);
|
|
||||||
if (value == null) {
|
|
||||||
return Response.status(404).
|
|
||||||
entity("media NOT FOUND: " + id).
|
|
||||||
type("text/plain").
|
|
||||||
build();
|
|
||||||
}
|
|
||||||
String filePathName = ConfigBaseVariable.getMediaDataFolder() + File.separator + id + File.separator + "data";
|
|
||||||
if ( value.mimeType.contentEquals("image/jpeg")
|
|
||||||
|| value.mimeType.contentEquals("image/png")
|
|
||||||
// || value.mimeType.contentEquals("image/webp")
|
|
||||||
) {
|
|
||||||
// reads input image
|
|
||||||
//System.out.println("Read path: " + filePathName);
|
|
||||||
File inputFile = new File(filePathName);
|
|
||||||
if (!inputFile.exists()) {
|
|
||||||
return Response.status(500).
|
|
||||||
entity("Internal Error: Media is NOT FOUNDABLE: " + id).
|
|
||||||
type("text/plain").
|
|
||||||
build();
|
|
||||||
}
|
|
||||||
BufferedImage inputImage = ImageIO.read(inputFile);
|
|
||||||
int scaledWidth = 250;
|
|
||||||
int scaledHeight = (int)((float)inputImage.getHeight() / (float)inputImage.getWidth() * (float) scaledWidth);
|
|
||||||
// creates output image
|
|
||||||
BufferedImage outputImage = new BufferedImage(scaledWidth,
|
|
||||||
scaledHeight, inputImage.getType());
|
|
||||||
|
|
||||||
// scales the input image to the output image
|
|
||||||
Graphics2D g2d = outputImage.createGraphics();
|
|
||||||
g2d.drawImage(inputImage, 0, 0, scaledWidth, scaledHeight, null);
|
|
||||||
g2d.dispose();
|
|
||||||
// create the output stream:
|
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
||||||
try {
|
|
||||||
// TODO: check how to remove buffer file !!! here, it is not needed at all...
|
|
||||||
ImageIO.write( outputImage, "JPG", baos);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return Response.status(500).
|
|
||||||
entity("Internal Error: resize fail: " + e.getMessage()).
|
|
||||||
type("text/plain").
|
|
||||||
build();
|
|
||||||
}
|
|
||||||
byte[] imageData = baos.toByteArray();
|
|
||||||
//Response.ok(new ByteArrayInputStream(imageData)).build();
|
|
||||||
Response.ResponseBuilder out = Response.ok(imageData)
|
|
||||||
.header(HttpHeaders.CONTENT_LENGTH, imageData.length);
|
|
||||||
out.type("image/jpeg");
|
|
||||||
// TODO: move this in a decorator !!!
|
|
||||||
CacheControl cc = new CacheControl();
|
|
||||||
cc.setMaxAge(3600);
|
|
||||||
cc.setNoCache(false);
|
|
||||||
out.cacheControl(cc);
|
|
||||||
return out.build();
|
|
||||||
}
|
|
||||||
return buildStream(filePathName, range, value.mimeType);
|
|
||||||
}
|
|
||||||
//@Secured
|
|
||||||
@GET
|
|
||||||
@Path("{id}/{name}")
|
|
||||||
@PermitTokenInURI
|
|
||||||
@RolesAllowed("USER")
|
|
||||||
@Produces(MediaType.APPLICATION_OCTET_STREAM)
|
|
||||||
public Response retriveDataFull(@Context SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) String token, @HeaderParam("Range") String range, @PathParam("id") Long id, @PathParam("name") String name) throws Exception {
|
|
||||||
GenericContext gc = (GenericContext) sc.getUserPrincipal();
|
|
||||||
//System.out.println("===================================================");
|
|
||||||
System.out.println("== DATA retriveDataFull ? id=" + id + " user=" + (gc==null?"null":gc.user));
|
|
||||||
//System.out.println("===================================================");
|
|
||||||
Data value = getSmall(id);
|
|
||||||
if (value == null) {
|
|
||||||
Response.status(404).
|
|
||||||
entity("media NOT FOUND: " + id).
|
|
||||||
type("text/plain").
|
|
||||||
build();
|
|
||||||
}
|
|
||||||
return buildStream(ConfigBaseVariable.getMediaDataFolder() + File.separator + id + File.separator + "data", range, value.mimeType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adapted from http://stackoverflow.com/questions/12768812/video-streaming-to-ipad-does-not-work-with-tapestry5/12829541#12829541
|
|
||||||
*
|
|
||||||
* @param range range header
|
|
||||||
* @return Streaming output
|
|
||||||
* @throws Exception IOException if an error occurs in streaming.
|
|
||||||
*/
|
|
||||||
private Response buildStream(final String filename, final String range, String mimeType) throws Exception {
|
|
||||||
File file = new File(filename);
|
|
||||||
//System.out.println("request range : " + range);
|
|
||||||
// range not requested : Firefox does not send range headers
|
|
||||||
if (range == null) {
|
|
||||||
final StreamingOutput output = new StreamingOutput() {
|
|
||||||
@Override
|
|
||||||
public void write(OutputStream out) {
|
|
||||||
try (FileInputStream in = new FileInputStream(file)) {
|
|
||||||
byte[] buf = new byte[1024 * 1024];
|
|
||||||
int len;
|
|
||||||
while ((len = in.read(buf)) != -1) {
|
|
||||||
try {
|
|
||||||
out.write(buf, 0, len);
|
|
||||||
out.flush();
|
|
||||||
//System.out.println("---- wrote " + len + " bytes file ----");
|
|
||||||
} catch (IOException ex) {
|
|
||||||
System.out.println("remote close connection");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException ex) {
|
|
||||||
throw new InternalServerErrorException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Response.ResponseBuilder out = Response.ok(output)
|
|
||||||
.header(HttpHeaders.CONTENT_LENGTH, file.length());
|
|
||||||
if (mimeType != null) {
|
|
||||||
out.type(mimeType);
|
|
||||||
}
|
|
||||||
return out.build();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
String[] ranges = range.split("=")[1].split("-");
|
|
||||||
final long from = Long.parseLong(ranges[0]);
|
|
||||||
|
|
||||||
//System.out.println("request range : " + ranges.length);
|
|
||||||
//Chunk media if the range upper bound is unspecified. Chrome, Opera sends "bytes=0-"
|
|
||||||
long to = CHUNK_SIZE + from;
|
|
||||||
if (ranges.length == 1) {
|
|
||||||
to = file.length() - 1;
|
|
||||||
} else {
|
|
||||||
if (to >= file.length()) {
|
|
||||||
to = (long) (file.length() - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final String responseRange = String.format("bytes %d-%d/%d", from, to, file.length());
|
|
||||||
//System.out.println("responseRange : " + responseRange);
|
|
||||||
final RandomAccessFile raf = new RandomAccessFile(file, "r");
|
|
||||||
raf.seek(from);
|
|
||||||
|
|
||||||
final long len = to - from + 1;
|
|
||||||
final MediaStreamer streamer = new MediaStreamer(len, raf);
|
|
||||||
Response.ResponseBuilder out = Response.ok(streamer)
|
|
||||||
.status(Response.Status.PARTIAL_CONTENT)
|
|
||||||
.header("Accept-Ranges", "bytes")
|
|
||||||
.header("Content-Range", responseRange)
|
|
||||||
.header(HttpHeaders.CONTENT_LENGTH, streamer.getLenth())
|
|
||||||
.header(HttpHeaders.LAST_MODIFIED, new Date(file.lastModified()));
|
|
||||||
if (mimeType != null) {
|
|
||||||
out.type(mimeType);
|
|
||||||
}
|
|
||||||
return out.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void undelete(Long id) throws Exception {
|
|
||||||
SqlWrapper.unsetDelete(Data.class, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
package org.kar.karideo.api;
|
|
||||||
|
|
||||||
import javax.ws.rs.InternalServerErrorException;
|
|
||||||
import javax.ws.rs.WebApplicationException;
|
|
||||||
import javax.ws.rs.core.StreamingOutput;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.io.RandomAccessFile;
|
|
||||||
|
|
||||||
public class MediaStreamer implements StreamingOutput {
|
|
||||||
private final int CHUNK_SIZE = 1024 * 1024; // 1MB chunks
|
|
||||||
final byte[] buf = new byte[CHUNK_SIZE];
|
|
||||||
private long length;
|
|
||||||
private RandomAccessFile raf;
|
|
||||||
|
|
||||||
public MediaStreamer(long length, RandomAccessFile raf) throws IOException {
|
|
||||||
//System.out.println("request stream of " + length / 1024 + " data");
|
|
||||||
if (length<0) {
|
|
||||||
throw new IOException("Wrong size of the file to stream: " + length);
|
|
||||||
}
|
|
||||||
this.length = length;
|
|
||||||
this.raf = raf;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(OutputStream outputStream) {
|
|
||||||
try {
|
|
||||||
while (length != 0) {
|
|
||||||
int read = raf.read(buf, 0, buf.length > length ? (int) length : buf.length);
|
|
||||||
try {
|
|
||||||
outputStream.write(buf, 0, read);
|
|
||||||
} catch (IOException ex) {
|
|
||||||
System.out.println("remote close connection");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
length -= read;
|
|
||||||
}
|
|
||||||
} catch (IOException ex) {
|
|
||||||
throw new InternalServerErrorException(ex);
|
|
||||||
} catch (WebApplicationException ex) {
|
|
||||||
throw new InternalServerErrorException(ex);
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
raf.close();
|
|
||||||
} catch (IOException ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
throw new InternalServerErrorException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getLenth() {
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -94,8 +94,8 @@ public class SeasonResource {
|
|||||||
out.name = name;
|
out.name = name;
|
||||||
out.parentId = seriesId;
|
out.parentId = seriesId;
|
||||||
out = SqlWrapper.insert(out);
|
out = SqlWrapper.insert(out);
|
||||||
return out;
|
|
||||||
}
|
}
|
||||||
|
return out;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// TODO Auto-generated catch block
|
// TODO Auto-generated catch block
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
@ -95,8 +95,8 @@ public class SeriesResource {
|
|||||||
out.name = name;
|
out.name = name;
|
||||||
out.parentId = typeId;
|
out.parentId = typeId;
|
||||||
out = SqlWrapper.insert(out);
|
out = SqlWrapper.insert(out);
|
||||||
return out;
|
|
||||||
}
|
}
|
||||||
|
return out;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// TODO Auto-generated catch block
|
// TODO Auto-generated catch block
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
@ -98,8 +98,8 @@ public class TypeResource {
|
|||||||
out = new Type();
|
out = new Type();
|
||||||
out.name = name;
|
out.name = name;
|
||||||
out = SqlWrapper.insert(out);
|
out = SqlWrapper.insert(out);
|
||||||
return out;
|
|
||||||
}
|
}
|
||||||
|
return out;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// TODO Auto-generated catch block
|
// TODO Auto-generated catch block
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
@ -2,16 +2,16 @@ package org.kar.karideo.api;
|
|||||||
|
|
||||||
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
|
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
|
||||||
import org.glassfish.jersey.media.multipart.FormDataParam;
|
import org.glassfish.jersey.media.multipart.FormDataParam;
|
||||||
import org.kar.karideo.WebLauncher;
|
import org.kar.archidata.model.Data;
|
||||||
import org.kar.archidata.db.DBEntry;
|
|
||||||
import org.kar.archidata.util.DataTools;
|
import org.kar.archidata.util.DataTools;
|
||||||
import org.kar.karideo.model.Data;
|
|
||||||
import org.kar.karideo.model.Media;
|
import org.kar.karideo.model.Media;
|
||||||
import org.kar.karideo.model.Season;
|
import org.kar.karideo.model.Season;
|
||||||
import org.kar.karideo.model.Series;
|
import org.kar.karideo.model.Series;
|
||||||
import org.kar.karideo.model.Type;
|
import org.kar.karideo.model.Type;
|
||||||
import org.kar.archidata.SqlWrapper;
|
import org.kar.archidata.SqlWrapper;
|
||||||
import org.kar.archidata.annotation.security.RolesAllowed;
|
import org.kar.archidata.annotation.security.RolesAllowed;
|
||||||
|
import org.kar.archidata.api.DataResource;
|
||||||
|
|
||||||
import javax.ws.rs.*;
|
import javax.ws.rs.*;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
@ -43,6 +43,7 @@ public class VideoResource {
|
|||||||
@RolesAllowed("ADMIN")
|
@RolesAllowed("ADMIN")
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
public Media put(@PathParam("id") Long id, String jsonRequest) throws Exception {
|
public Media put(@PathParam("id") Long id, String jsonRequest) throws Exception {
|
||||||
|
System.out.println("update video " + id + " ==> '" + jsonRequest + "'");
|
||||||
SqlWrapper.update(Media.class, id, jsonRequest);
|
SqlWrapper.update(Media.class, id, jsonRequest);
|
||||||
return SqlWrapper.get(Media.class, id);
|
return SqlWrapper.get(Media.class, id);
|
||||||
}
|
}
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
package org.kar.karideo.model;
|
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
import org.kar.archidata.annotation.SQLComment;
|
|
||||||
import org.kar.archidata.annotation.SQLIfNotExists;
|
|
||||||
import org.kar.archidata.annotation.SQLLimitSize;
|
|
||||||
import org.kar.archidata.annotation.SQLNotNull;
|
|
||||||
import org.kar.archidata.annotation.SQLTableName;
|
|
||||||
import org.kar.archidata.model.GenericTable;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
|
||||||
|
|
||||||
@SQLTableName ("data")
|
|
||||||
@SQLIfNotExists
|
|
||||||
@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
|
|
||||||
public class Data extends GenericTable {
|
|
||||||
|
|
||||||
@SQLNotNull
|
|
||||||
@SQLLimitSize(128)
|
|
||||||
@SQLComment("Sha512 of the data")
|
|
||||||
public String sha512;
|
|
||||||
@SQLNotNull
|
|
||||||
@SQLLimitSize(128)
|
|
||||||
@SQLComment("Mime -type of the media")
|
|
||||||
public String mimeType;
|
|
||||||
@SQLNotNull
|
|
||||||
@SQLComment("Size in Byte of the data")
|
|
||||||
public Long size;
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
package org.kar.karideo.model;
|
|
||||||
|
|
||||||
public class DataGetToken {
|
|
||||||
public String login;
|
|
||||||
public String method;
|
|
||||||
public String time;
|
|
||||||
public String password;
|
|
||||||
}
|
|
@ -168,7 +168,10 @@ export class HttpWrapperService {
|
|||||||
server = environment.defaultServer;
|
server = environment.defaultServer;
|
||||||
}
|
}
|
||||||
const basePage = environment.server[server];
|
const basePage = environment.server[server];
|
||||||
let addressServerRest = `${basePage }/`;
|
let addressServerRest = basePage;
|
||||||
|
if (!basePage.endsWith("/")) {
|
||||||
|
addressServerRest = `${basePage}/`;
|
||||||
|
}
|
||||||
let options = inputOptions;
|
let options = inputOptions;
|
||||||
if(isNullOrUndefined(options)) {
|
if(isNullOrUndefined(options)) {
|
||||||
options = {};
|
options = {};
|
||||||
@ -177,8 +180,12 @@ export class HttpWrapperService {
|
|||||||
if (isArrayOfs(api, isString, isNumber, isBoolean)) {
|
if (isArrayOfs(api, isString, isNumber, isBoolean)) {
|
||||||
for (let iii=0; iii<api.length; iii++) {
|
for (let iii=0; iii<api.length; iii++) {
|
||||||
let elem = api[iii];
|
let elem = api[iii];
|
||||||
|
if (out.endsWith("/")) {
|
||||||
|
out += elem;
|
||||||
|
} else {
|
||||||
out += `/${elem}`;
|
out += `/${elem}`;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
out += api;
|
out += api;
|
||||||
}
|
}
|
||||||
@ -346,6 +353,7 @@ export class HttpWrapperService {
|
|||||||
|
|
||||||
// Complex wrapper to simplify interaction:s
|
// Complex wrapper to simplify interaction:s
|
||||||
putSpecific(urlPath: UrlPath, data: object):Promise<ModelResponseHttp> {
|
putSpecific(urlPath: UrlPath, data: object):Promise<ModelResponseHttp> {
|
||||||
|
console.log(`Put on ${urlPath}`);
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.request({
|
this.request({
|
||||||
endPoint: urlPath,
|
endPoint: urlPath,
|
||||||
|
Loading…
Reference in New Issue
Block a user