How to force Google Storage file to have `content-disposition: inline` instead of `attachment`? - google-cloud-storage

I'm using GoogleApi.Storage.V1.Api.Objects.storage_objects_insert_simple to upload files to Google Storage. However, it seems that no matter what I do, the content-disposition HTTP response header is being set to attachment which forces browsers to download a file rather than displaying it in a browser tab.
Here's my usage:
{:ok, object} =
GoogleApi.Storage.V1.Api.Objects.storage_objects_insert_simple(
gconn,
"my-uploads-bucket",
"multipart",
%{
name: Path.basename(path),
contentType: content_type,
contentDisposition: "inline" # <-- this doesn't seem to have an effect
},
path
)
Does anyone know how to force the content disposition header to have a value of inline?
Update
After inspecting the value of object, I'm seeing the following:
%GoogleApi.Storage.V1.Model.Object{
acl: nil,
bucket: "my-uploads-bucket",
cacheControl: nil,
componentCount: nil,
contentDisposition: "inline", # <-- !!!
contentEncoding: nil,
contentLanguage: nil,
contentType: "image/png",
crc32c: "MGa01Q==",
...
However, after fetching a file via curl -I <url>, I see the following:
HTTP/2 200
...
content-type: image/png
content-disposition: attachment
...

It seems like my mistake here was in using the mediaLink field as the URL. This URL even has download as part of the URL: https://storage.googleapis.com/download/storage/....
If I instead use name field to build the URL, then it works as expected.
send_resp(
conn,
200,
"https://storage.googleapis.com/my-bucket/" <> object.name
)

Related

Mailparser ignores attachments' headers

I'm sending an email with attachments, which look like
const attachment = { filename, path, size, headers: { uid: 'someId' } };
According to the Nodemailer's docs I can set attachment's headers in the same format as message headers.
At the receiver's side the email is parsed by simpleParser (from mailparser). Parsed email looks great, it has all attachment's info but the headers are empty {}. But the raw email source has the following:
----_NmP-30615c8ac620489d-Part_1
Content-Type: image/jpeg; name=attachment.jpg
Uid: someId
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=attachment.jpg
The uid header is there, but it is lost after parsing.
Also tried headers in the following format: headers: [{ header: 'uid', value: 'someId' }]. But it doesn't help.
How can I get that headers correctly? Or this can be the mailparser's bug?
After few hours of mailparser's source code researching I discovered that mailparser returns headers as Map, that's why it looks like Object {} which seems to be empty, but it's not.

Empty request body alamofire multipart

I would like to make such request via alamofire with attaching selected file:
I also have to add interceptor to the request. So, I added below code to my image picker method:
let manager = Session(configuration: URLSessionConfiguration.default, interceptor: CallInterceptor.init(method:HTTPMethod.post))
manager.upload(multipartFormData: { multipartFormData in
multipartFormData.append( fileUrl, withName: "upload_doc\"; filename= \"\(fileName)\"")
},
to:Pathes.init(endpoint: "user/photo").resourseUrl.absoluteString).responseJSON(completionHandler: { (completion) in
print(completion.debugDescription)
})
where:
let fileName = fileUrl.lastPathComponent
in logs I saw such output:
[Request]: POST url
[Headers]:
Authorization: Bearer token
Content-Type: multipart/form-data; boundary=alamofire.boundary.c5da18c6b053a9d7
[Body]: None
[Response]: None
[Network Duration]: None
[Serialization Duration]: 3.253298928029835e-05s
[Result]: failure(Alamofire.AFError.sessionDeinitialized)
as I see I didn't attach request body what caused such request cancel. How I can add body to the request? I thought that this line:
multipartFormData.append( fileUrl, withName: "upload_doc\"; filename= \"\(fileName)\"")
but it doesn't add body to the request. I also tried adding file directly:
multipartFormData.append(imgData, withName: "upload_doc\"; filename= \"\(fileName)\"",fileName: fileName, mimeType: "image/jpg")
but this way also sent:
[Body]: None
so the question is how to add some body to multipart request or I have to send file in another way?
The error, .sessionDeinitialized, means that your Session instance was deinited before the request completed. You need to keep it alive, either as an instance value in something else, or a singleton.

request formData to API, gets “Network Error” in axios while uploading image

I am making a POST request to server to upload an image and sending formdata using axios in react-native. i am getting "Network Error". i also try fetch but nothing work.using react native image picker libeary for select image.in postman api working fine
formData.append('title', Title);
formData.append('class_id', selectClass._id)
formData.append('subject_id', checkSelected)
formData.append('teacher_id', userId)
formData.append('description', lecture);
formData.append('type', 'image');
var arr=[];
arr.push(imageSource)
arr.map((file,index)=>{
formData.append('file',{
uri:file.path,
type:file.type,
name:file.name
})
})
axios({
method: 'post',
url: URL + 'admin/assignment/create',
data: data,
headers: {
"content-type": "multipart/form-data",
'x-auth-token': token,
},
})
.then(function (response) {
//handle success
console.log('axios assigment post',response);
})
.catch(function (response) {
//handle error
console.log('axios assigment post',response);
});
Project keeps flipper java file under app > source > debug in react native > 0.62. There is an issue with Flipper Network that causes the problem in your case. If you remove the debug folder, you will not be able to debug Android with Flipper, so the best solution is upgrading Flipper version in android > gradle.properties to 0.46.0 that fixes the problem.
You can change it with this line
FLIPPER_VERSION=0.46.0
react-nativeandroid
The issue that I was facing which is close to what you are mentioning is that I was getting NetworkError when using image-picker and trying to upload the file using axios. It was working perfectly in iOS but not working in android.
This is how I solved the issue.
There are two independent issues at action here. Let’s say we get imageUri from image-picker, then we would use these following lines of code to upload from the frontend.
const formData = new FormData();
formData.append('image', {
uri : imageUri,
type: "image",
name: imageUri.split("/").pop()
});
The first issue is with the imageUri itself. If let’s say photo path is /user/.../path/to/file.jpg. Then file picker in android would give imageUri value as file:/user/.../path/to/file.jpg whereas file picker in iOS would give imageUri value as file:///user/.../path/to/file.jpg.
The solution for the first issue is to use file:// instead of file: in the formData in android.
The second issue is that we are not using proper mime-type. It is working fine on iOS but not on Android. What makes this worse is that the file-picker package gives the type of the file as “image” and it does not give proper mime-type.
The solution is to use proper mime-type in the formData in the field type. Ex: mime-type for .jpg file would be image/jpeg and for .png file would be image/png. We do not have to do this manually. Instead, you can use a very famous npm package called mime.
The final working solution is:
import mime from "mime";
const newImageUri = "file:///" + imageUri.split("file:/").join("");
const formData = new FormData();
formData.append('image', {
uri : newImageUri,
type: mime.getType(newImageUri),
name: newImageUri.split("/").pop()
});
I hope this helps to solve your problem :)
REACT NATIVE SOLUTION
If you are using Axios or Fetch in React Native and you got Network Error when uploading the file or data.
Try to commenting below line from the /android/app/src/main/java/com/{your_project}/MainApplication.java
its located around the 40-50 line
initializeFlipper(this, getReactNativeHost().getReactInstanceManager())
https://github.com/facebook/react-native/issues/28551
I faced the same issue. The following steps worked for me.
update FLIPPER_VERSION=0.52.0 latest
for formData code as below:
let formData = new FormData();
let file = {
uri: brand.uri,
type: 'multipart/form-data',
name: brand.uri
};
formdata.append('logo', file);
The type must be 'multipart/form-data' as the post header.
"react-native": "0.62.1",
"react": "16.11.0",
"axios": "^0.19.2",
weird solution i have to delete debug folder
in android ->app->source->debug
and restart the app again
its solve my problem. i think it's cache problem.
I had this problem and solve it via commenting the 43 line in
android/src/debug/.../.../ReactNativeFlipper.java
// builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
could you test it?
in my case, the solution was to change to
const headers = {
accept: 'application/json',
'content-type': 'multipart/form-data',
};
change this line: form_data.append('file', data);
To form_data.append('file', JSON.stringify(data));
from https://github.com/react-native-image-picker/react-native-image-picker/issues/798
You need to add this uesCleartextTraffic="true" to the AndroidManifest.xml file found inside the dir android/app/src/main/AndroidManifest.xml
<application ... android:usesCleartextTraffic="true"> Then, Because of issue with Flipper Network.
I commented initializeFlipper(this, getReactNativeHost().getReactInstanceManager())
in this file /android/app/src/main/java/com/{your_project}/MainApplication.java
Also, commenting out line number 43 in this file android/app/src/debug/java/com/**/ReactNativeFlipper.java
line43: builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
If using expo and expo-image-picker, then the problem is only with the image type and nothing else.
In the latest updates, they removed the bug related to path (as other answers mention to change the beginning of the path which was correct for the older versions).
Now to remove the problem, we need to change the type only and is mentioned by other answers to use mime which works fine;
import mime from 'mime'
const data = new FormData();
data.append('image', {
uri: image.uri,
name: image.uri.split('/').pop() // getting the text after the last slash which is the name of the image
type: mime.getType(image.uri) // image.type returns 'image' but mime.getType(image.uri) returns 'image/jpeg' or whatever is the type
})
In my case, after debbuging for a while, the issue was in nginx.
The image was "too big".
I Had to add annotations to the Kubernetes ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: 20m
....
It was a bit tricky to debug since the request never got through the load balancer (nginx) to the Api service.
The "Network error" message didn't help a lot either.
I am using Expo SDK 42 (react-native v0.63). And I was using the expo-document-picker library to pick the documents & upload to server.
This is the code I am using to open the picker & get the metadata about the file.
const result = await DocumentPicker.getDocumentAsync({
type: 'image/*',
copyToCacheDirectory: false,
multiple: false,
});
if (result.type === 'success') {
const name_split = result.name.split('.');
const ext = name_split[name_split.length - 1];
// result.uri = 'file://' + result.uri;
result.type = helper.get_mime_type(ext);
delete result.size;
}
(You can write your function to get the mime type from the file extension or use some library from npm)
And this is the code I am using to upload the file to server:
const formdata = new FormData();
formdata.append('custom_param', 'value');
formdata.append('file', result); // 'result' is from previous code snippet
const headers = {
accept: 'application/json',
'content-type': 'multipart/form-data',
};
const opts = {
method: 'POST',
url: 'your backend endpoint',
headers: headers,
data: formdata,
};
return await axios.request(axiosopts);
The above code is the working code. I want to explain what I did wrong initially that was causing the Network Error in axios.
I had set copyToCacheDirectory to true initially and the uri I was getting in the result was in the format /data/path/to/file.jpeg. And I also tried appending file:// to beginning of the uri but it didn't work.
I then set copyToCacheDirectory to false and the uri I was getting in the result was in the format content://path/to/file.jpeg. And this didn't cause any Network Error in axios.
I have faced this error as well. I found out that I got this error because the file path is wrong and axios couldn't find the file. It is a weird error message when the uri is wrong though but that's what actually has happened. So double checking the uri of the file would fix the issue. Mainly consider file://.
I faced the same issue.
After capturing the photo wait for 10sec then start uploading. It worked for me.
Also got the same issue. I spent almost 4 days to find reason.
So in my case it was Content-Type: multipart/form-data. I forgot
indicate it. In Android it should be indicated explicitly...
After two days of searching for a solution, I found that the problem was in my rn-fetch-blob library, Changed it to in package.json dependencies
"rn-fetch-blob": "^0.12.0",
fix my Netowk issue and app crash on uploading. I am using
react-native-image-crop-picker
always send image and file in formdata in post api through axios in react js and react native
Blockquote
REACT NATIVE SOLUTION USING AXIOS
I face the same issue after upgrading react native cli project.
I'm using
"react-native": "0.70.6",
"react": "18.1.0",
"axios": "^1.1.3"
AND FLIPPER_VERSION=0.125.0
The below code solves my issue
const imageData = image;
const form = new FormData();
form.append("ProfileImage", {
type: imageData.mime,
uri: imageData.path,
name: imageData.path.split("/").pop(),
});
axios({
method: "put",
url: `${URL}/api/UploadPhoto`,
data: formData,
headers: {
"Content-Type": "multipart/form-data",
"cache-control": "no-cache",
},
processData: false,
contentType: false,
mimeType: "multipart/form-data",
});
For me, I didn't comment on any line from the /android/app/src/main/java/com/{your_project}/MainApplication.java
initializeFlipper(this, getReactNativeHost().getReactInstanceManager())
Also not changed FLIPPER_VERSION

How do I add metadata to a file when uploading via a GCS signedUploadURL?

I'm using a signed URL (generated via the GCS PHP API) to upload a file to a bucket. I POST the signed URL, which returns a Location header which, in turn, I do a PUT of to do the actual upload. This basic upload process is working fine. Now, I need to pass some metadata (uploader name, uploader email, notes, etc.) along with the file.
According to the documentation, I add headers to the PUT request, of the form 'x-goog-meta-<name>': 'value', which are supposed to become the metadata. However, if I don't add them to the signed URL and the POST request, I get a spurious CORS error (No 'Access-Control-Allow-Origin' header is present on the requested resource).
The upload succeeds, and I can see in the Network tab of Chrome that these headers are getting added to the POST and PUT requests, but when the file hits the bucket, there is no metadata.
Here's my code, somewhat simplified:
var metadata, headers;
metadata =
{
'sender-name': "bob dobbs",
'sender-email': "bobdobbs#zynyz.com",
};
headers = { 'x-goog-resumable': 'start' };
for (var i in metadata)
{
headers['x-goog-meta-' + i] = metadata[i];
}
// Start the upload
$.ajax(
{
type: POST,
url: signed_upload_url,
success: on_init_success,
contentType: file.type,
headers: headers,
processData: false
});
// Do the actual upload
var on_init_success = function(result, status, xhr)
{
var loc;
loc = xhr.getResponseHeader('Location');
if (loc)
{
$.ajax(
{
type: 'PUT',
url: loc,
data: file,
contentType: file.type,
headers: headers,
processData: false
});
}
}

GCS Signed URL Post Object using XMLHttpRequest with progress listener results in browser sending Options instead of POST

I have successfully implemented upload of an Object using multi-part Post request with Signature, Policy document GCS POST ...etc from the browser using XMLHttpRequest and angular $http .
But when I attach event listener on XMLHttpRequest upload to show a progress bar to the user, the browser sends a Options Method instead of POST. storage.googleapis.com returns 200 ok After that I was expecting a POST to be sent from the browser with the file but that did not happen. Without the upload listener the code works perfectly. Should I move to PUT ? any workaround
factory.uploadFileToUrlXHR = function(file,obj){
var deferred = $q.defer();
var fd = new FormData();
fd.append('key', obj.key);
fd.append('Content-Type',obj.contenttype)
fd.append('GoogleAccessId', obj.googleaccessId);
fd.append('policy', obj.policy);
fd.append('signature', obj.signature);
fd.append('file', file);
var XHR = new XMLHttpRequest();
XHR.addEventListener('load', function(event) {
// alert('Yeah! Data sent and response loaded.');
deferred.resolve(event);
});
XHR.upload.addEventListener("progress",function(evt){
if (evt.lengthComputable) {
$log.info("add upload event-listener" + evt.loaded + "/" + evt.total);
}
}, false);
// Define what happens in case of error
XHR.addEventListener('error', function(event) {
//alert('Oups! Something went wrong.');
deferred.resolve(event);
});
// Set up our request
XHR.open('POST', obj.uri);
// Send our FormData object; HTTP headers are set automatically
XHR.send(fd);
return deferred.promise;
}
I resolved the issue ,
When you use POST from the browser to upload a file to Google Cloud storage append the bucket name to the URL . In the below code obj.uri should be "https://storage.googleapis.com/bucketname
XHR.open('POST', obj.uri); and remove the bucket name from the key. Key should contain the object name.
fd.append('key', obj.key);
if you do not append the bucket name as part of the POST URL, the browser will send the Options request to https://storage.googleapis.com/. GCS will not be able to find the right CORS configuration. CORS configuration is mapped to the bucket Name.
Sample CORS configuration i had applied.
[
{
"origin": ["http://localhost:8282"],
"method": ["OPTIONS","PUT","POST","GET", "HEAD", "DELETE"],
"responseHeader": ["Content-Type"],
"maxAgeSeconds": 3600
}
]