Skip to content

Commit

Permalink
Stream file instead of using temp file
Browse files Browse the repository at this point in the history
  • Loading branch information
tylerjmchugh committed Dec 30, 2024
1 parent f4232fa commit c5193fa
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,19 @@
import org.fao.geonet.kernel.datamanager.IMetadataUtils;
import org.fao.geonet.repository.MetadataRepository;
import org.fao.geonet.util.FileUtil;
import org.fao.geonet.util.LimitedInputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.web.multipart.MultipartFile;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
Expand All @@ -65,7 +64,7 @@ public abstract class AbstractStore implements Store {
private static final Logger log = LoggerFactory.getLogger(AbstractStore.class);

@Value("${api.params.maxUploadSize}")
private int maxUploadSize;
protected int maxUploadSize;

@Override
public final List<MetadataResource> getResources(final ServiceContext context, final String metadataUuid, final Sort sort,
Expand Down Expand Up @@ -266,24 +265,8 @@ public final MetadataResource putResource(ServiceContext context, String metadat
FileUtil.humanizeFileSize(maxUploadSize)});
}

Path tempFilePath = Files.createTempFile("uploaded_resource", null);
try (BoundedInputStream boundedInputStream = new BoundedInputStream(fileUrl.openStream(), maxUploadSize+1)) {
Files.copy(boundedInputStream, tempFilePath, StandardCopyOption.REPLACE_EXISTING);
}

if (Files.size(tempFilePath) > maxUploadSize) {
Files.deleteIfExists(tempFilePath);
throw new GeonetMaxUploadSizeExceededException("uploadedResourceSizeExceededException")
.withMessageKey("exception.maxUploadSizeExceeded",
new String[]{FileUtil.humanizeFileSize(maxUploadSize)})
.withDescriptionKey("exception.maxUploadSizeExceededUnknownSize.description",
new String[]{FileUtil.humanizeFileSize(maxUploadSize)});
}

try (InputStream is = new FileInputStream(tempFilePath.toFile())) {
try (InputStream is = new LimitedInputStream(fileUrl.openStream(), maxUploadSize+1)) {
return putResource(context, metadataUuid, filename, is, null, visibility, approved);
} finally {
Files.deleteIfExists(tempFilePath);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
package org.fao.geonet.api.records.attachments;

import jeeves.server.context.ServiceContext;
import org.fao.geonet.api.exception.GeonetMaxUploadSizeExceededException;
import org.fao.geonet.api.exception.ResourceAlreadyExistException;
import org.fao.geonet.api.exception.ResourceNotFoundException;
import org.fao.geonet.constants.Geonet;
Expand All @@ -35,6 +36,8 @@
import org.fao.geonet.kernel.GeonetworkDataDirectory;
import org.fao.geonet.kernel.setting.SettingManager;
import org.fao.geonet.lib.Lib;
import org.fao.geonet.util.FileUtil;
import org.fao.geonet.util.LimitedInputStream;
import org.fao.geonet.utils.IO;
import org.fao.geonet.utils.Log;
import org.springframework.beans.factory.annotation.Autowired;
Expand Down Expand Up @@ -203,6 +206,14 @@ public MetadataResource putResource(final ServiceContext context, final String m
checkResourceId(filename);
Path filePath = getPath(context, metadataId, visibility, filename, approved);
Files.copy(is, filePath, StandardCopyOption.REPLACE_EXISTING);
if (is instanceof LimitedInputStream && ((LimitedInputStream) is).isLimitReached()) {
Files.deleteIfExists(filePath);
throw new GeonetMaxUploadSizeExceededException("uploadedResourceSizeExceededException")
.withMessageKey("exception.maxUploadSizeExceeded",
new String[]{FileUtil.humanizeFileSize(maxUploadSize)})
.withDescriptionKey("exception.maxUploadSizeExceededUnknownSize.description",
new String[]{FileUtil.humanizeFileSize(maxUploadSize)});
}
if (changeDate != null) {
IO.touch(filePath, FileTime.from(changeDate.getTime(), TimeUnit.MILLISECONDS));
}
Expand Down
102 changes: 102 additions & 0 deletions core/src/main/java/org/fao/geonet/util/LimitedInputStream.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package org.fao.geonet.util;

import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;

public class LimitedInputStream extends InputStream {
private final InputStream in;
private final long max;
private long pos;
private long mark;
private boolean propagateClose;

public LimitedInputStream(InputStream in, long size) {
this.pos = 0L;
this.mark = -1L;
this.propagateClose = true;
this.max = size;
this.in = in;
}

public LimitedInputStream(InputStream in) {
this(in, -1L);
}

public int read() throws IOException {
if (this.max >= 0L && this.pos >= this.max) {
return -1;
} else {
int result = this.in.read();
++this.pos;
return result;
}
}

public int read(byte[] b) throws IOException {
return this.read(b, 0, b.length);
}

public int read(byte[] b, int off, int len) throws IOException {
if (this.max >= 0L && this.pos >= this.max) {
return -1;
} else {
long maxRead = this.max >= 0L ? Math.min((long)len, this.max - this.pos) : (long)len;
int bytesRead = this.in.read(b, off, (int)maxRead);
if (bytesRead == -1) {
return -1;
} else {
this.pos += (long)bytesRead;
return bytesRead;
}
}
}

public long skip(long n) throws IOException {
long toSkip = this.max >= 0L ? Math.min(n, this.max - this.pos) : n;
long skippedBytes = this.in.skip(toSkip);
this.pos += skippedBytes;
return skippedBytes;
}

public int available() throws IOException {
return this.max >= 0L && this.pos >= this.max ? 0 : this.in.available();
}

public boolean isLimitReached() {
return this.pos >= this.max;
}

public String toString() {
return this.in.toString();
}

public void close() throws IOException {
if (this.propagateClose) {
this.in.close();
}

}

public synchronized void reset() throws IOException {
this.in.reset();
this.pos = this.mark;
}

public synchronized void mark(int readlimit) {
this.in.mark(readlimit);
this.mark = this.pos;
}

public boolean markSupported() {
return this.in.markSupported();
}

public boolean isPropagateClose() {
return this.propagateClose;
}

public void setPropagateClose(boolean propagateClose) {
this.propagateClose = propagateClose;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ api.exception.unsatisfiedRequestParameter=Unsatisfied request parameter
api.exception.unsatisfiedRequestParameter.description=Unsatisfied request parameter.
exception.maxUploadSizeExceeded=Maximum upload size of {0} exceeded.
exception.maxUploadSizeExceeded.description=The request was rejected because its size ({0}) exceeds the configured maximum ({1}).
exception.maxUploadSizeExceededUnknownSize.description=The request was rejected because its size exceeds the configured maximum ({0}).
exception.resourceNotFound.metadata=Metadata not found
exception.resourceNotFound.metadata.description=Metadata with UUID ''{0}'' not found.
exception.resourceNotFound.resource=Metadata resource ''{0}'' not found
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ api.exception.unsatisfiedRequestParameter=Param\u00E8tre de demande non satisfai
api.exception.unsatisfiedRequestParameter.description=Param\u00E8tre de demande non satisfait.
exception.maxUploadSizeExceeded=La taille maximale du t\u00E9l\u00E9chargement de {0} a \u00E9t\u00E9 exc\u00E9d\u00E9e.
exception.maxUploadSizeExceeded.description=La demande a \u00E9t\u00E9 refus\u00E9e car sa taille ({0}) exc\u00E8de le maximum configur\u00E9 ({1}).
exception.maxUploadSizeExceededUnknownSize.description=La demande a \u00E9t\u00E9 refus\u00E9e car sa taille exc\u00E8de le maximum configur\u00E9 ({0}).
exception.resourceNotFound.metadata=Fiches introuvables
exception.resourceNotFound.metadata.description=La fiche ''{0}'' est introuvable.
exception.resourceNotFound.resource=Ressource ''{0}'' introuvable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ api.exception.unsatisfiedRequestParameter=Unsatisfied request parameter
api.exception.unsatisfiedRequestParameter.description=Unsatisfied request parameter.
exception.maxUploadSizeExceeded=Maximum upload size of {0} exceeded.
exception.maxUploadSizeExceeded.description=The request was rejected because its size ({0}) exceeds the configured maximum ({1}).
exception.maxUploadSizeExceededUnknownSize.description=The request was rejected because its size exceeds the configured maximum ({0}).
exception.resourceNotFound.metadata=Metadata not found
exception.resourceNotFound.metadata.description=Metadata with UUID ''{0}'' not found.
exception.resourceNotFound.resource=Metadata resource ''{0}'' not found
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ api.exception.unsatisfiedRequestParameter=Param\u00E8tre de demande non satisfai
api.exception.unsatisfiedRequestParameter.description=Param\u00E8tre de demande non satisfait.
exception.maxUploadSizeExceeded=La taille maximale du t\u00E9l\u00E9chargement de {0} a \u00E9t\u00E9 exc\u00E9d\u00E9e.
exception.maxUploadSizeExceeded.description=La demande a \u00E9t\u00E9 refus\u00E9e car sa taille ({0}) exc\u00E8de le maximum configur\u00E9 ({1}).
exception.maxUploadSizeExceededUnknownSize.description=La demande a \u00E9t\u00E9 refus\u00E9e car sa taille exc\u00E8de le maximum configur\u00E9 ({0}).
exception.resourceNotFound.metadata=Fiches introuvables
exception.resourceNotFound.metadata.description=La fiche ''{0}'' est introuvable.
exception.resourceNotFound.resource=Ressource ''{0}'' introuvable
Expand Down

0 comments on commit c5193fa

Please sign in to comment.