forked from DSpace/DSpace
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathMetadataBitstreamController.java
More file actions
158 lines (141 loc) · 6.41 KB
/
Copy pathMetadataBitstreamController.java
File metadata and controls
158 lines (141 loc) · 6.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.Objects;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.logging.log4j.Logger;
import org.dspace.app.rest.exception.UnprocessableEntityException;
import org.dspace.app.rest.model.BitstreamRest;
import org.dspace.app.rest.model.ItemRest;
import org.dspace.app.rest.utils.ContextUtil;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Bitstream;
import org.dspace.content.Bundle;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.content.service.BitstreamService;
import org.dspace.core.Context;
import org.dspace.handle.service.HandleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* CLARIN Controller for downloading individual bitstreams from Items.
* This controller provides endpoints to download specific bitstreams by name
* without creating ZIP archives, allowing users to download multiple files
* separately instead of as a single compressed archive.
*
* @author DSpace Community
*/
@RestController
@RequestMapping("/api/" + ItemRest.CATEGORY + "/" + BitstreamRest.PLURAL_NAME)
public class MetadataBitstreamController {
private static final Logger log = org.apache.logging.log4j.LogManager
.getLogger(MetadataBitstreamController.class);
@Autowired
private BitstreamService bitstreamService;
@Autowired
private HandleService handleService;
/**
* Downloads a specific bitstream by name from an Item identified by its handle.
* This method allows downloading individual files based on their exact names
* without creating a ZIP archive.
*
* @param handleId The handle identifier of the Item containing the bitstream
* @param name The exact name of the bitstream to download
* @param request The HTTP servlet request
* @param response The HTTP servlet response where the bitstream content will be written
* @throws SQLException if there is a database access error
* @throws IOException if there is an I/O error during the download process
* @throws UnprocessableEntityException if the handle does not resolve to a valid Item
* or if the bitstream with the specified name is not found
*/
@PreAuthorize("hasPermission(#handleId, 'ITEM', 'READ')")
@GetMapping("/handle/{handleId}/{name}")
public void downloadBitstreamByName(
@PathVariable String handleId,
@PathVariable String name,
HttpServletRequest request,
HttpServletResponse response) throws SQLException, IOException {
Context context = ContextUtil.obtainContext(request);
try {
DSpaceObject dso = handleService.resolveToObject(context, handleId);
if (Objects.isNull(dso)) {
throw new UnprocessableEntityException("No DSpace object found for handle: " + handleId);
}
if (!(dso instanceof Item)) {
throw new UnprocessableEntityException("The handle does not resolve to an Item: " + handleId);
}
Item item = (Item) dso;
Bitstream targetBitstream = findBitstreamByName(item, name);
if (Objects.isNull(targetBitstream)) {
throw new UnprocessableEntityException(
"No bitstream with name '" + name + "' found in Item " + item.getID());
}
// Set response headers for file download
response.setContentType(targetBitstream.getFormat(context).getMIMEType());
response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + targetBitstream.getName() + "\"");
// Stream the bitstream content to the response
try (InputStream is = bitstreamService.retrieve(context, targetBitstream)) {
streamBitstreamToResponse(is, response);
} catch (AuthorizeException e) {
log.error("Authorization error while retrieving bitstream: {}", targetBitstream.getName(), e);
throw new RuntimeException("Access denied to bitstream: " + targetBitstream.getName(), e);
}
} finally {
if (context != null) {
context.complete();
}
}
}
/**
* Finds a bitstream by name within an Item's ORIGINAL bundles.
* This method searches through all ORIGINAL bundles of the item to locate
* a bitstream with the exact matching name.
*
* @param item The Item to search for bitstreams
* @param name The exact name of the bitstream to find
* @return The matching Bitstream object, or null if not found
*/
private Bitstream findBitstreamByName(Item item, String name) {
for (Bundle bundle : item.getBundles("ORIGINAL")) {
for (Bitstream bitstream : bundle.getBitstreams()) {
if (name.equals(bitstream.getName())) {
return bitstream;
}
}
}
return null;
}
/**
* Streams bitstream content to the HTTP response output stream.
* Uses a buffered approach for efficient streaming of large files.
*
* @param inputStream The input stream containing the bitstream data
* @param response The HTTP response to write the data to
* @throws IOException if an I/O error occurs during streaming
*/
private void streamBitstreamToResponse(InputStream inputStream, HttpServletResponse response)
throws IOException {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
response.getOutputStream().write(buffer, 0, bytesRead);
}
response.getOutputStream().flush();
}
}