I'm using the Google Drive Python API (v3), and I'm having trouble updating a file on Drive.
Here is my code for updating a file. The code runs without any errors, and even returns the correct file ID at the end, but it does not update the file on Drive.
basename = "myfile.txt"
filename = "/path/to/myfile.txt"
file_metadata = {'name': basename}
media = MediaFileUpload(filename, mimetype='text/plain', resumable=True)
file = drive_service.files().update(fileId=file_id, body=file_metadata, media_body=media).execute()
print(file)
The print(file) statement produces the following output:
{'id': <fileid>}
What's curious is that I'm able to create a file without any issues. Here is my code for creating a file, which creates a file successfully.
basename = "myfile.txt"
filename = "/path/to/myfile.txt"
file_metadata = {'name': basename, 'parents': [drive_folder_id]}
media = MediaFileUpload(filename, mimetype='text/plain')
file = drive_service.files().create(body=file_metadata, media_body=media, fields='id').execute()
Why am I able to create a file, but not update a file?
How can I improve my code for updating a file so that it successfully updates the file on Drive?
Summary:
My code for updating a file runs smoothly, but it doesn't do the thing it's intended to do, that is, update the contents of the file. How can I update the contents of a file on Drive using the Google Drive Python API v3?
It seems that the body parameter should point to a file resource object, not a plain dictionary as in your code. See this example from the documentation
try:
# First retrieve the file from the API.
file = service.files().get(fileId=file_id).execute()
# File's new metadata.
file['title'] = new_title
file['description'] = new_description
file['mimeType'] = new_mime_type
# File's new content.
media_body = MediaFileUpload(
new_filename, mimetype=new_mime_type, resumable=True)
# Send the request to the API.
updated_file = service.files().update(
fileId=file_id,
body=file,
newRevision=new_revision,
media_body=media_body).execute()
return updated_file
except errors.HttpError, error:
print 'An error occurred: %s' % error
return None
I think the problem may have been my web browser's caching feature. I was looking at files that I updated and I didn't see any changes, and this may have been caused by caching.
I switched browsers and tested, and it seems to be the case.
The update method works for me now.
Related
I have read through several articles/docs/questions now and each solves a part of my problem, but I just can't seem to put them all together to work. I am using Flask/Python, the Google Drive API v3 and a Service Account, but want to run the site on Heroku, which is setup already. Since Heroku doesn't have a storage for files though, I need to directly upload the files into Google Drive, which would need to be done sooner or later anyway.
Basically what I want to do is:
People type in their data on a website and upload a file.
That file then gets renamed according to the user's data (needs to support Chinese characters/utf8)
then the renamed file gets uploaded onto Google Drive using a service account
I was able to rename the file with utf-8 characters and save it locally, but don't know how to upload it to Google.I have tried a way earlier, but I don't know whether just a file has been created with no content or the file has been uploaded properly,I do see files and folders when listing the files/folders (that doesn't work now though, as I think my attempt now is exactly the same). My Code:
def upload_service(file, filename):
file_metadata = {
'name': filename,
'parents': 'Upload'
}
mime = mimetypes.guess_type(file.filename)[0]
media = MediaFileUpload(filename, mimetype=mime)
cloudFile = service.files().create(body=file_metadata, media_body=media).execute()
result = service.files().list().execute()
In here, file is the actual file uploaded with the filename being the input the user typed in.
All works fine, but for media = MediaFileUpload(filename, mimetype=mime) I get a FileNotFoundError: [Errno 2] No such file or directory: 'gtmuCfr.jpg' error, stating that the file with the name of filename or file are not there. This goes for the original name of the file and the new name.
Ok, I finally found the issue and was able to fix it (for the most part).
with open(new_name.encode("utf-8"), "wb") as save_file:
file = save_file.write(f.read())
upload_service(file, new_name)
doing it like this solves the issue as file is the int where it is stored (I guess), but since it doesn't have a filename attribute, I need to use the path/new_name inside the function.
The only difference inside the function is:
file_metadata = {
'name': path,
'parents': 'Upload'
}
mime = mimetypes.guess_type(path)[0]
media = MediaFileUpload(path, mimetype=mime)
where I need to use the path/new_name that I sent as a second parameter.
Now the only issue left is with the Upload folder/parent not being selected for the upload, but I can see the files (after giving permission) in my Drive and they have the right content.
I'm quite new on Django and i'm looking for a way to dwonload a zip file from my django site but i have some issue when i'm running this piece of code:
def download(self):
dirName = settings.DEBUG_FOLDER
name = 'test.zip'
with ZipFile(name, 'w') as zipObj:
# Iterate over all the files in directory
for folderName, subfolders, filenames in os.walk(dirName):
for filename in filenames:
# create complete filepath of file in directory
filePath = os.path.join(folderName, filename)
# Add file to zip
zipObj.write(filePath, basename(filePath))
path_to_file = 'http://' + sys.argv[-1] + '/' + name
resp= {}
# Grab ZIP file from in-memory, make response with correct MIME-type
resp = HttpResponse(content_type='application/zip')
# ..and correct content-disposition
resp['Content-Disposition'] = 'attachment; filename=%s' % smart_str(name)
resp['X-Sendfile'] = smart_str(path_to_file)
return resp
I get:
Exception Value:
<HttpResponse status_code=200, "application/zip"> is not JSON serializable
I tried to change the content_type to octet-stream but it doesn't work
And to use a wrapper as followw:
wrapper = FileWrapper(open('test.zip', 'rb'))
content_type = 'application/zip'
content_disposition = 'attachment; filename=name'
# Grab ZIP file from in-memory, make response with correct MIME-type
resp = HttpResponse(wrapper, content_type=content_type)
# ..and correct content-disposition
resp['Content-Disposition'] = content_disposition
I didn't find useful answer so far but maybe I didn't search well, so if it seems my problem had been already traited, feel free to notify me
Thank you very much for any help
You have to send the zip file as byte
response = HttpResponse(zipObj.read(), content_type="application/zip")
response['Content-Disposition'] = 'attachment; filename=%s' % smart_str(name)
return response
I would do like this:
(Caveat I use wsl so the python function will make use of cmd lines)
In view:
import os
def zipdownfun(request):
""" Please establish in settings.py where media file should be downloaded from.
In my case is media with a series of other folders inside. Media folder is at the same level of project root folder, where settings.py is"""
file_name = os.path.join(MEDIA_URL,'folder_where_your_file_is','file_name.zip')
"""let us put the case that you have zip folder in media folder"""
file_folder_path = os.path.join(MEDIA_URL,'saving_folder')
"""The command line takes as first variable the name of the
future zip file and as second variable the destination folder"""
cmd = f'zip {file_name} {file_folder_path}'
"""With os I open a process in the background so that some magic
happens"""
os.system(cmd)
"""I don't know what you want to do with this, but I placed the
URL of the file in a button for the download, so you will need
the string of the URL to place in href of an <a> element"""
return render(request,'your_html_file.html', {'url':file_name})
The db I have created, will be updated very often. I used a slightly different version of this function with -r clause since I had to zip, each time, a folder. Why I did this? The database I have created has to allow the download of this zipped folder. This folder will be updated daily. So this function basically overwrites the file each time that is downloaded. It will be so fresh of new data each time.
Please refer to this page to understand how to create a button for the download of the generated file.
Take as reference approach 2. The URL variable that you are passing to the Django template should be used at the place of the file (screenshot attached)
I hope it can help!
I'd like to periodically download and upload a file from a shared teams drive on google drive. I can upload to the folder, but not download.
This is what I've tried
team_drive_id = 'YYY'
file_to_download= 'ZZZ'
parent_folder_id = 'XXX'
f = drive.CreateFile({
'id':file_to_download,
'parents': [{
'kind': 'drive#fileLink',
'teamDriveId': team_folder_id,
'id': parent_drive_id
}]
})
f= drive.CreateFile({'id': file_to_download})
f.GetContentFile('test.csv', mimetype='text/csv')
But this is what I get:
ApiRequestError: <HttpError 404 when requesting https://www.googleapis.com/drive/v2/files/file_to_download?alt=json returned "File not found: file_to_download">
Any suggestions?
Following the documentation that can be seen here
First you create the file:
f = drive.CreateFile({'id': file_to_download})
then you set the content on the file
f.SetContentString("whatever comes inside this file, it is a csv so you know what to expect to be here")
and to finish the upload you need to do
f.Upload()
after that, the file is properly created there, you can read it using the GetContentString method
I am using Python 2.7 and Reportlab to create .pdf files for display/print in my app engine system. I am using ndb.Model to store the data if that matters.
I am able to produce the equivalent of a bank statement for a single client on-line. That is; the user clicks the on-screen 'pdf' button and the .pdf statement appears on screen in a new tab, exactly as it should.
I am using the following code to save .pdf files to Google Cloud Storage successfully
buffer = StringIO.StringIO()
self.p = canvas.Canvas(buffer, pagesize=portrait(A4))
self.p.setLineWidth(0.5)
try:
# create .pdf of .csv data here
finally:
self.p.save()
pdfout = buffer.getvalue()
buffer.close()
filename = getgcsbucket() + '/InvestorStatement.pdf'
write_retry_params = gcs.RetryParams(backoff_factor=1.1)
try:
gcs_file = gcs.open(filename,
'w',
content_type='application/pdf',
retry_params=write_retry_params)
gcs_file.write(pdfout)
except:
logging.error(traceback.format_exc())
finally:
gcs_file.close()
I am using the following code to create a list of all files for display on-screen, it shows all the files stored above.
allfiles = []
bucket_name = getgcsbucket()
rfiles = gcs.listbucket(bucket_name)
for rfile in rfiles:
allfiles.append(rfile.filename)
return allfiles
My screen (html) shows rows of ([Delete] and Filename). When the user clicks the [Delete] button, the following delete code snippet works (filename is /bucket/filename, complete)
filename = self.request.get('filename')
try:
gcs.delete(filename)
except gcs.NotFoundError:
pass
My question - given I have a list of files on-screen, I want the user to click on the filename and for that file to be downloaded to the user's computer. In Google's Chrome Browser, this would result in the file being downloaded, with it's name displayed on the bottom left of the screen.
One other point, the above example is for .pdf files. I will also have to show .csv files in the list and would like them to be downloaded as well. I only want the files to be downloaded, no display is required.
So, I would like a snippet like ...
filename = self.request.get('filename')
try:
gcs.downloadtousercomputer(filename) ???
except gcs.NotFoundError:
pass
I think I have tried everything I can find both here and elsewhere. Sorry I have been so long-winded. Any hints for me?
To download a file instead of showing it in the browser, you need to add a header to your response:
self.response.headers["Content-Disposition"] = 'attachment; filename="%s"' % filename
You can specify the filename as shown above and it works for any file type.
One solution you can try is to read the file from the bucket and print the content as the response with the correct header:
import cloudstorage
...
def read_file(self, filename):
bucket_name = "/your_bucket_name"
file = bucket_name + '/' + filename
with cloudstorage.open(file) as cloudstorage_file:
self.response.headers["Content-Disposition"] = str('attachment;filename=' + filename)
contents = cloudstorage_file.read()
cloudstorage_file.close()
self.response.write(contents)
Here filename could be something you are sending as GET parameter and needs to be a file that exist on your bucket or you will raise an exception.
[1] Here you will find a sample.
[1]https://cloud.google.com/appengine/docs/standard/python/googlecloudstorageclient/read-write-to-cloud-storage
I am trying to download a file using Google Drive (in python), edit some values, saving the file locally and then upload that local file as an update. I am getting the HTML version of the file and uploading using mimeType = "text/html". The edits and uploads work except for the margins or line spacing on headings (h2 & h3) increase slightly each time the script is run.
I have tried putting the content directly into the local file after the download without editing it and the same thing happens (see code below). Has anyone any ideas as to what might be causing this?
KB
# get the google drive api object
service = get_service()
# search for file
params = {}
params["q"] = "title = 'Test File'"
values = service.files().list(**params).execute()
# get file
found_file = values['items'][0]
# download file content
content = download_file(service, found_file, "text/html")
f = open('temp.html', 'wb')
f.write(content)
f.close()
file = service.files().get(fileId=found_file['id']).execute()
# set the new values
media_body = MediaFileUpload("temp.html", mimetype="text/html",
resumable=True)
try:
# update the file
updated_file = service.files().update(fileId=found_file['id'], body=file,
media_body=media_body).execute()
except:
print("error uploading file")